Initial commit of Usergrid Javascript SDK into its own repo
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..485dee6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.idea
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..a2b7b6f
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,139 @@
+module.exports = function(grunt) {
+	var files = [
+        "lib/modules/util/Event.js",
+        "lib/modules/util/Logger.js",
+        "lib/modules/util/Promise.js",
+        "lib/modules/util/Ajax.js",
+        "lib/Usergrid.js",
+		"lib/modules/Client.js",
+		"lib/modules/Entity.js",
+		"lib/modules/Collection.js",
+		"lib/modules/Group.js",
+		"lib/modules/Counter.js",
+		"lib/modules/Folder.js",
+		"lib/modules/Asset.js",
+		"lib/modules/Error.js"
+	];
+	var tests = ["tests/mocha/index.html", "tests/mocha/test_*.html"];
+	// Project configuration.
+	grunt.initConfig({
+        //pkg: grunt.file.readJSON('package.json'),
+        "meta": {
+            "package": grunt.file.readJSON("package.json")
+        },
+        "clean": ["usergrid.js", "usergrid.min.js"],
+        "uglify": {
+            "unminified": {
+                "options": {
+                    "banner": "/*! \n\
+ *Licensed to the Apache Software Foundation (ASF) under one\n\
+ *or more contributor license agreements.  See the NOTICE file\n\
+ *distributed with this work for additional information\n\
+ *regarding copyright ownership.  The ASF licenses this file\n\
+ *to you under the Apache License, Version 2.0 (the\n\
+ *\"License\"); you may not use this file except in compliance\n\
+ *with the License.  You may obtain a copy of the License at\n\
+ *\n\
+ *  http://www.apache.org/licenses/LICENSE-2.0\n\
+ * \n\
+ *Unless required by applicable law or agreed to in writing,\n\
+ *software distributed under the License is distributed on an\n\
+ *\"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n\
+ *KIND, either express or implied.  See the License for the\n\
+ *specific language governing permissions and limitations\n\
+ *under the License.\n\
+ * \n\
+ * \n\
+ * <%= meta.package.name %>@<%= meta.package.version %> <%= grunt.template.today('yyyy-mm-dd') %> \n\
+ */\n",
+                    "mangle": false,
+                    "compress": false,
+                    "beautify": true,
+                    "preserveComments": function(node, comment){
+                        //console.log((node.parent_scope!==undefined&&comment.value.indexOf('*Licensed to the Apache Software Foundation')===-1)?"has parent":comment.value);
+                        return comment.type==='comment2'&&comment.value.indexOf('*Licensed to the Apache Software Foundation')===-1;
+                    }
+                },
+                "files": {
+                    "usergrid.js": files
+                }
+            },
+            "minified": {
+                "options": {
+                    "banner": "/*! \n\
+ *Licensed to the Apache Software Foundation (ASF) under one\n\
+ *or more contributor license agreements.  See the NOTICE file\n\
+ *distributed with this work for additional information\n\
+ *regarding copyright ownership.  The ASF licenses this file\n\
+ *to you under the Apache License, Version 2.0 (the\n\
+ *\"License\"); you may not use this file except in compliance\n\
+ *with the License.  You may obtain a copy of the License at\n\
+ *\n\
+ *  http://www.apache.org/licenses/LICENSE-2.0\n\
+ * \n\
+ *Unless required by applicable law or agreed to in writing,\n\
+ *software distributed under the License is distributed on an\n\
+ *\"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n\
+ *KIND, either express or implied.  See the License for the\n\
+ *specific language governing permissions and limitations\n\
+ *under the License.\n\
+ * \n\
+ * \n\
+ * <%= meta.package.name %>@<%= meta.package.version %> <%= grunt.template.today('yyyy-mm-dd') %> \n\
+ */\n",
+                    "mangle": false,
+                    "compress": true,
+                    "beautify": false,
+                    "preserveComments": "some"
+                },
+                "files": {
+                    "usergrid.min.js": files
+                }
+            }
+        },
+        "connect": {
+            "server": {
+                "options": {
+                    "port": 3000,
+                    "base": "."
+                }
+            },
+            "test": {
+                "options": {
+                    "port": 8000,
+                    "base": "."
+                }
+            }
+        },
+        "watch": {
+            "files": [files, 'Gruntfile.js'],
+            "tasks": ["default"]
+        },
+        "blanket_mocha": {
+            //"all": tests,
+            urls: [ 'http://localhost:8000/tests/mocha/index.html' ],
+            "options": {
+                "dest": "report/coverage.html",
+                "reporter": "Spec",
+                "threshold": 70
+            }
+        }
+    });
+	grunt.loadNpmTasks("grunt-contrib-clean");
+	grunt.loadNpmTasks("grunt-contrib-uglify");
+	grunt.loadNpmTasks("grunt-contrib-watch");
+	grunt.loadNpmTasks("grunt-contrib-connect");
+	grunt.loadNpmTasks("grunt-blanket-mocha");
+	grunt.registerTask("default", [
+		"clean",
+		"uglify"
+	]);
+	grunt.registerTask("dev", [
+		"connect:server",
+		"watch"
+	]);
+	grunt.registerTask("test", [
+		"connect:test",
+		"blanket_mocha"
+	]);
+};
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e06d208
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   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.
+
diff --git a/README.md b/README.md
new file mode 100755
index 0000000..41a2b5a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,868 @@
+# Usergrid JavaScript SDK
+
+##Quickstart
+Detailed instructions follow but if you just want a quick example of how to get started with this SDK, here’s a minimal HTML5 file that shows you how to include & initialize the SDK, as well as how to read & write data from Usergrid with it.
+
+```html
+<!DOCTYPE html>
+<html>
+	<head>
+		<!-- Don't forget to include the SDK -->
+		<script src="/path/to/usergrid.js"></script>
+		<!-- You can find the file in the Usergrid github repo
+			 https://github.com/usergrid/usergrid/blob/master/sdks/html5-javascript/usergrid.js -->
+
+		<script type="text/javascript">
+
+		
+			// Initializing the SDK
+			var client = new Usergrid.Client({
+				orgName:'yourorgname', // Your Usergrid organization name 
+				appName:'sandbox' // Your Usergrid app name
+			});
+
+			// Make a new "book" collection and read data
+			var options = {
+				type:'books',
+				qs:{ql:'order by created DESC'}
+			}
+
+			var books;
+
+			client.createCollection(options, function (err, collection) {
+				books = collection;
+				if (err) {
+					alert("Couldn't get the list of books.");
+				} else {
+					while(books.hasNextEntity()) {
+						var book = books.getNextEntity();
+						alert(book.get("title")); // Output the title of the book
+					}
+				}
+			});
+
+			// Uncomment the next 4 lines if you want to write data
+
+			// book = { "title": "the old man and the sea" };
+			// books.addEntity(book, function (error, response) {
+			// 	if (error) { alert("Couldn't add the book.");
+			// 	} else { alert("The book was added."); } });
+		</script>
+	</head>
+	<body></body>
+</html>
+```
+
+##Version
+
+Current Version: **0.11.0**
+
+See change log:
+
+<https://github.com/usergrid/usergrid/blob/master/sdks/html5-javascript/changelog.md>
+
+##Comments / questions
+For help using this SDK, reach out on the Usergrid google group:
+
+https://groups.google.com/forum/?hl=en#!forum/usergrid
+
+Or just open github issues. 
+
+
+
+##Overview
+This open source SDK simplifies writing JavaScript / HTML5 applications that connect to Usergrid. The repo is located here:
+
+<https://github.com/apache/usergrid-javascript>
+
+To find out more about Usergrid, see:
+
+<http://usergrid.apache.org>
+
+To view the Usergrid documentation, see:
+
+<http://usergrid.apache.org/docs/>
+
+
+##Node.js
+Want to use Node.js? No problem - use the Usergrid Node Module:
+
+<https://npmjs.org/package/usergrid>
+
+or on github:
+
+<https://github.com/apache/usergrid-nodejs>
+
+The syntax for this Javascript SDK and the Usergrid Node module are almost exactly the same so you can easily transition between them.
+
+
+##About the samples
+This SDK comes with a variety of samples that you can use to learn how to connect your project to App Services (Usergrid).
+
+**Note:** All the sample code in this file is pulled directly from the "test" example, so you can rest-assured that it will work! You can find it here:
+
+<https://github.com/apache/usergrid-nodejs/examples/test/test.html>
+
+
+##Installing
+Once you have downloaded the SDK, add the usergrid.js file to your project. This file is located in the root of the SDK. Include it in the top of your HTML file (in between the head tags):
+
+	<script src="path/to/usergrid.js" type="text/javascript"></script>
+
+##Getting started
+You are now ready to create a new `Client` object, which is the main entry point in the SDK:
+
+	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
+	});
+
+The last two items are optional. The `logging` option will enable console.log output from the client, and will output various bits of information (calls that are being made, errors that happen).  The `buildCurl` option will cause cURL equivalent commands of all calls to the API to be displayed in the console.log output (see more about this below).
+
+You are now ready to use the `Client` object to make calls against the API.
+
+##Asynchronous vs. synchronous calls (a quick discussion)
+This SDK works by making RESTful API calls from your application to the App Services (Usergrid) API. This SDK currently only supports asynchronous calls. 
+
+If an API call is _synchronous_, it means that code execution will block (or wait) for the API call to return before continuing. This SDK does not yet support synchronous calls.
+
+_Asynchronous_ calls, which are supported by this SDK, do not block (or wait) for the API call to return from the server. Execution continues on in your program, and when the call returns from the server, a "callback" function is executed. For example, in the following code, the function `dogCreateCallback` will be called when the `createEntity` call returns from the server.  Meanwhile, execution will continue.
+
+	function dogCreateCallback(err, dog) {
+		alert('I will probably be called second');
+		if (err) {
+			// Error - Dog not created
+		} else {
+			// Success - Dog was created
+		}
+	}
+	
+	client.createEntity({type:'dogs'}, dogCreateCallback);
+	
+	alert('I will probably be called first');
+
+The result of this is that we cannot guarantee the order of the two alert statements.  Most likely, the alert right after the `createEntity` function will be called first because the API call will take a second or so to complete.
+
+The important point is that program execution will continue asynchronously, the callback function will be called once program execution completes.
+
+##Entities and collections
+Usergrid stores its data as _entities_ in _collections_.  Entities are essentially JSON objects and collections are just like folders for storing these objects. You can learn more about entities and collections in the App Services docs:
+
+<http://usergrid.apache.org/docs/introduction/data-model.html>
+
+
+##Entities
+You can easily create new entities, or access existing ones. Here is a simple example that shows how to create a new entity of type "dogs":
+
+	var options = {
+		type:'dogs',
+		name:'einstein'
+	}
+
+	client.createEntity(options, function (err, dog) {
+		if (err) {
+			//Error - Dog not created
+		} else {
+			//Success - Dog was created
+		}
+	});
+
+**Note:** All calls to the API will be executed asynchronously, so it is important that you use a callback.
+
+Once your object is created, you can update properties on it by using the `set` method, then save it back to the database using the `save` method:
+
+	// Once the dog is created, you can set single properties (key, value)
+	dog.set('breed','mutt');
+
+	// The set function can also take a JSON object
+	var data = {
+		master:'Doc',
+		state:'hungry'
+	}
+
+	// Set is additive, so previously set properties are not overwritten unless a property with the same name exists in the data object
+	dog.set(data);
+
+	// And save back to the database
+	dog.save(function(err){
+		if (err){
+			// Error - dog not saved
+		} else {
+			// Success - dog was saved
+		}
+	});
+
+**Note:** Using the `set` function will set the properties locally. Make sure you call the `save` method on the entity to save them back to the database!
+
+You can also refresh the object from the database if needed (in case the data has been updated by a different client or device) by using the `fetch` method.  Use the `get` method to retrieve properties from the object:
+
+	// 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
+
+			// Get single properties from the object using the get method
+			var master = dog.get('master');
+			var name = dog.get('name');
+
+			// Or, get all the data as a JSON object:
+			var data = dog.get();
+
+			// Based on statements above, the data object should look like this:
+			/*
+			{
+				type:'dogs',
+				name:'einstein',
+				master:'Doc',
+				state:'hungry',
+				breed:'mutt'
+			}
+			*/
+
+		}
+	});
+
+Use the `destroy` method to remove the entity from the database:
+
+	// 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 was removed from database
+			dog = null;
+			// No real dogs were harmed!
+		}
+	});
+
+##The collection object
+The collection object models collections in the database. Once you start programming your app, you will likely find that this is a useful method of interacting with the database.  Creating a collection will automatically populate the object with entities from the collection.
+
+The following example shows how to create a collection object, then how to use those entities once they have been populated from the server:
+
+	// Options object needs to have the type (which is the collection type)
+	// ”qs” is for “query string”. “ql” is for “query language”
+	var options = {
+		type:'dogs',
+		qs:{ql:'order by index'}
+	}
+
+	client.createCollection(options, function (err, 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();
+				// Do something with the entity
+				var name = dog.get('name');
+			}
+
+		}
+	});
+
+You can also add a new entity of the same type to the collection:
+
+	// 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
+		}
+	});
+
+##Collection iteration and paging
+At any given time, the collection object will have one page of data loaded. To get the next page of data from the server and iterate across the entities, use the following pattern:
+
+	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, so do something with it
+				while(dogs.hasNextEntity()) {
+					// Get a reference to the dog
+					dog = dogs.getNextEntity();
+					// Do something with the entity
+					var name = dog.get('name');
+				}
+			}
+		});
+	}
+
+You can use the same pattern with the `hasPreviousPage` and `getPreviousPage` methods to get a previous page of data.
+
+By default, the database will return 10 entities per page.  Use the `qs` (query string) property (more about this below) to set a different limit (up to 999):
+
+	var options = {
+		type:'dogs',
+		qs:{limit:50} // Set a limit of 50 entities per page
+	}
+
+	client.createCollection(options, function (err, 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();
+				// Do something with the entity
+				var name = dog.get('name');
+			}
+
+			// Wait! What if we want to display them again??
+			// Simple! Just reset the entity pointer:
+			dogs.resetEntityPointer();
+			while(dogs.hasNextEntity()) {
+				// Get a reference to the dog
+				var dog = dogs.getNextEntity();
+				// Do something with the entity
+				var name = dog.get('name');
+			}
+
+		}
+	});
+
+Several convenience methods exist to make working with pages of data easier:
+
+* `resetPaging` - Calls made after this will get the first page of data (the cursor, which points to the current page of data, is deleted)
+* `getFirstEntity` - Gets the first entity of a page
+* `getLastEntity` - Gets the last entity of a page
+* `resetEntityPointer` - Sets the internal pointer back to the first element of the page
+* `getEntityByUUID` - Returns the entity if it is in the current page
+
+###Custom Queries
+A custom query allows you to tell the API that you want your results filtered or altered in some way.
+
+Use the `qs` property in the options object - "qs" stands for "query string".  By adding a JSON object of key/value pairs to the `qs` property of the options object, you signal that you want those values used for the key/value pairs in the query string that is sent to the server.
+
+For example, to specify that the query results should be ordered by creation date, add the `qs` parameter to the options object:
+
+	var options = {
+		type:'dogs',
+		qs:{ql:'order by created DESC'}
+	};
+
+The `qs` object above will be converted into a query language call that looks like this:
+
+	/dogs?ql=order by created DESC
+
+If you also wanted to get more entities in the result set than the default 10, say 100, you can specify a query similar to the following (the limit can be a maximum of 999):
+
+	dogs.qs = {ql:'order by created DESC',limit:'100'};
+
+The `qs` object above will be converted into a call that looks like this:
+
+	/dogs?ql=order by created DESC&limit=100
+
+**Note**: There are many cases where expanding the result set is useful. But be careful -- the more results you get back in a single call, the longer it will take to transmit the data back to your app.
+
+If you need to change the query on an existing object, simply access the `qs` property directly:
+
+	dogs.qs = {ql:'order by created DESC'};
+
+Then make your fetch call:
+
+	dogs.fetch(...)
+
+Another common requirement is to limit the results to a specific query.  For example, to get all brown dogs, use the following syntax:
+
+	dogs.qs = {ql:"select * where color='brown'"};
+
+You can also limit the results returned such that only the fields you specify are returned:
+
+	dogs.qs = {'ql':"select name, age where color='brown'"};
+
+**Note:** In the two preceding examples that we put single quotes around 'brown', so it will be searched as a string.
+
+You can find more information on custom queries here:
+
+<http://usergrid.apache.org/docs/query-language/>
+##Counters
+Counters can be used by an application to create custom statistics, such as how many times an a file has been downloaded or how many instances of an application are in use.
+
+###Create the counter instance
+
+**Note:** The count is not altered upon instantiation
+
+	var counter = new Usergrid.Counter({
+			client: client,
+			data: {
+				category: 'usage',
+				//a timestamp of '0' defaults to the current time
+				timestamp: 0,
+				counters: {
+					running_instances: 0,
+					total_instances: 0
+				}
+			}
+		}, function(err, data) {
+			if (err) { 
+			   // Error - there was a problem creating the counter
+			} else { 
+				// Success - the counter was created properly
+			}
+		});
+
+###Updating counters
+
+When an application starts, we want to increment the 'running_instances' and 'total_instances' counters.
+
+	// add 1 running instance
+	counter.increment({
+			name: 'running_instances',
+			value: 1
+		}, function(err, data) {
+			// ...
+		});
+
+	// add 1 total instance
+	counter.increment({
+			name: 'total_instances',
+			value: 1
+		}, function(err, data) {
+			// ...
+		});
+
+When the application exits, we want to decrement 'running_instances'
+
+	// subtract 1 total instance
+	counter.decrement({
+			name: 'total_instances',
+			value: 1
+		}, function(err, data) {
+			// ...
+		});
+
+Once you have completed your testing, you can reset these values to 0.
+
+	counter.reset({
+			name: 'total_instances'
+		}, function(err, data) {
+			// ...
+		});
+
+##Assets
+
+Assets can be attached to any entity as binary data. This can be used by your application to store images and other file types. There is a limit of one asset per entity.
+
+####Attaching an asset
+
+An asset can be attached to any entity using Enity.attachAsset(file, callback). You can also call attachAsset() on the same entity to change the asset attached to it.
+
+	//Create a new entity to attach an asset to - you can also use an existing entity
+	var properties = {
+	    type:'user',
+	    username:'someUser', 
+	};
+
+	dataClient.createEntity(properties, function(err, response, entity) {
+		if (!err) {
+			//The entity was created, so call attachAsset() on it.
+			entity.attachAsset(file, function(err, response){
+				if (!err){
+					//Success - the asset was attached
+				} else {
+					//Error
+				}
+			});
+		}
+	});
+
+##Retrieving Assets
+
+To retrieve the data, call Entity.downloadAsset(callback). A blob is returned
+in the success callback.
+
+	entity.downloadAsset(function(err, file){
+		if (err) { 
+			// Error - there was a problem retrieving the data
+		} else { 
+			// Success - the asset was downloaded			
+
+			// Create an image tag to hold our downloaded image data
+			var img = document.createElement("img");
+			
+			// Create a FileReader to feed the image
+			// into our newly-created element
+			var reader = new FileReader();
+			reader.onload = (function(aImg) { 
+					return function(e) {
+						aImg.src = e.target.result;
+					}; 
+				})(img);
+			reader.readAsDataURL(file);
+			
+			// Append the img element to our page
+			document.body.appendChild(img);
+		} 
+	})
+
+
+
+##Modeling users with the entity object
+Use the entity object to model users.  Simply specify a type of "users".
+
+**Note:** Remember that user entities use "username", not "name", as the distinct key.
+Here is an example:
+
+	// Type is 'users', set additional parameters as needed.
+	var options = {
+		type:'users',
+		username:'marty',
+		password:'mysecurepassword',
+		name:'Marty McFly',
+		city:'Hill Valley'
+	}
+
+	client.createEntity(options, function (err, marty) {
+		if (err){
+			//Error - user not created
+		} else {
+			//Success - user created
+			var name = marty.get('name');
+		}
+	});
+
+If the user is modified, just call save on the user again:
+
+	// 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
+		}
+	});
+
+To refresh the user's information in the database:
+
+	marty.fetch(function(err){
+		if (err){
+			// Error - not refreshed
+		} else {
+			// Success - user refreshed
+
+			// Do something with the entity
+			var girlfriend = marty.get('girlfriend');
+		}
+	});
+
+###To sign up a new user
+When a new user wants to sign up in your app, simply create a form to catch their information, then use the `client.signup` method:
+
+	// Method signature: client.signup(username, password, email, name, callback)
+	client.signup('marty', 'mysecurepassword', 'marty@timetravel.com', 'Marty McFly',
+		function (err, marty) {
+			if (err){
+				error('User not created');
+				runner(step, marty);
+			} else {
+				success('User created');
+				runner(step, marty);
+			}
+		}
+	);
+
+
+###To log a user in
+Logging a user in means sending the user's username and password to the server, and getting back an access (OAuth) token. You can then use this token to make calls to the API on the user's behalf. The following example shows how to log a user in and log them out:
+
+	username = 'marty';
+	password = 'mysecurepassword';
+	client.login(username, password, function (err) {
+		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 logged in user entity this way:
+			client.getLoggedInUser(function(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');
+				}
+			});
+		}
+	});
+
+If you need to change a user's password, set the `oldpassword` and `newpassword` fields, then call save:
+
+	marty.set('oldpassword', 'mysecurepassword');
+	marty.set('newpassword', 'mynewsecurepassword');
+	marty.save(function(err){
+		if (err){
+			// Error - user password not updated
+		} else {
+			// Success - user password updated
+		}
+	});
+
+To log a user out, call the `logout` function:
+
+	client.logout();
+
+	// verify the logout worked
+	if (client.isLoggedIn()) {
+		// Error - logout failed
+	} else {
+		// Success - user has been logged out
+	}
+
+###Making connections
+Connections are a way to connect two entities with a word, typically a verb.  This is called an _entity relationship_.  For example, if you have a user entity with username of marty, and a dog entity with a name of einstein, then using our RESTful API, you could make a call like this:
+
+	POST users/marty/likes/dogs/einstein
+
+This creates a one-way connection between marty and einstein, where marty "likes" einstein.
+
+Complete documentation on the entity relationships API can be found here:
+
+<http://usergrid.apache.org/docs/relationships/>
+
+For example, say we have a new dog named einstein:
+
+	var options = {
+		type:'dogs',
+		name:'einstein',
+		breed:'mutt'
+	}
+
+	client.createEntity(options, function (err, dog) {
+		if (err) {
+			// Error - new dog not created
+		} else {
+			// Success - new dog created
+		}
+	});
+
+Then, we can create a "likes" connection between our user, Marty, and the dog named einstein:
+
+	marty.connect('likes', dog, function (err, data) {
+		if (err) {
+			// Error - connection not created
+		} else {
+			// Success - the connection call succeeded
+			// Now let’s do a getConnections call to verify that it worked
+			marty.getConnections('likes', function (err, data) {
+				if (err) {
+					// Error - could not get connections
+				} else {
+					// Success - got all the connections
+					// Verify that connection exists
+					if (marty.likes.einstein) {
+						// Success - connection exists
+					} else {
+						// Error - connection does not exist
+					}
+				}
+			});
+		}
+	});
+
+We could have just as easily used any other word as the connection (e.g. "owns", "feeds", "cares-for", etc.).
+
+Now, if you want to remove the connection, do the following:
+
+	marty.disconnect('likes', dog, function (err, data) {
+		if (err) {
+			// Error - connection not deleted
+		} else {
+			// Success - the connection has been deleted
+			marty.getConnections('likes', function (err, data) {
+				if (err) {
+					// Error - error getting connections
+				} else {
+					// Success! - now verify that the connection exists
+					if (marty.likes.einstein) {
+						// Error - connection still exists
+					} else {
+						// Success - connection deleted
+					}
+				}
+			});
+		}
+	});
+
+If you no longer need the object, call the `destroy` method and the object will be deleted from database:
+
+	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
+		}
+	});
+
+##Making generic calls
+If you find that you need to make calls to the API that fall outside of the scope of the entity and collection objects, you can use the following format to make any REST calls against the API:
+
+	client.request(options, callback);
+
+This format allows you to make almost any call against the Usergrid API. For example, to get a list of users:
+
+	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
+		}
+	});
+
+Or, to create a new user:
+
+	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
+		}
+	});
+
+Or, to update a user:
+
+	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
+		}
+	});
+
+Or, to delete a user:
+
+	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
+		}
+	});
+
+The `options` object for the `client.request` function includes the following:
+
+* `method` - HTTP method (`GET`, `POST`, `PUT`, or `DELETE`), defaults to `GET`
+* `qs` - object containing querystring values to be appended to the URI
+* `body` - object containing entity body for POST and PUT requests
+* `endpoint` - API endpoint, for example "users/fred"
+* `mQuery` - boolean, set to `true` if running management query, defaults to `false`
+* `buildCurl` - boolean, set to `true` if you want to see equivalent curl commands in console.log, defaults to `false`
+
+You can make any call to the API using the format above.  However, in practice using the higher level entity and collection objects will make life easier as they take care of much of the heavy lifting.
+
+
+###Validation
+An extension for validation is provided for you to use in your apps.  The file is located here:
+
+	/extensions/usergrid.session.js
+
+Include this file at the top of your HTML file - AFTER the SDK file:
+
+	<script src="path/to/usergrid.js" type="text/javascript"></script>
+	<script src="path/to/extensions/usergrid.validation.js" type="text/javascript"></script>
+
+A variety of functions are provided for verifying many common types such as usernames, passwords, and so on.  Feel free to copy and modify these functions for use in your own projects.
+
+
+###cURL
+[cURL](http://curl.haxx.se/) is an excellent way to make calls directly against the API. As mentioned in the **Getting started** section of this guide, one of the parameters you can add to the new client `options` object is **buildCurl**:
+
+	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
+	});
+
+If you set this parameter to `true`, the SDK will build equivalent `curl` commands and send them to the console.log window.
+
+More information on cURL can be found here:
+
+<http://curl.haxx.se/>
+
+## Contributing
+We welcome your enhancements!
+
+Like [Usergrid](https://github.com/apache/usergrid-javascript), the Usergrid Javascript SDK is open source and licensed under the Apache License, Version 2.0.
+
+1. Fork it.
+2. Create your feature branch (`git checkout -b my-new-feature`).
+3. Commit your changes (`git commit -am 'Added some feature'`).
+4. Push your changes to the upstream branch (`git push origin my-new-feature`).
+5. Create new Pull Request (make sure you describe what you did and why your mod is needed).
+
+###Contributing to usergrid.js
+usergrid.js and usergrid.min.js are built from modular components using Grunt. If you want to contribute updates to these files, please commit your changes to the modules in /lib/modules. Do not contribute directly to usergrid.js or your changes could get overwritten in a future build.
+
+##More information
+For more information on Usergrid, visit <http://usergrid.apache.org/>.
+
+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.
+
+
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 0000000..692a1a4
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,76 @@
+##Change log
+###0.11.0
+- Removed 'getOnExist' flag from createEntity and createGroup. Handling of duplicate entity errors is now the responsibility of the client.
+- Usergrid.Group.prototype.fetch returns self instead of group list
+- Usergrid.Client.prototype.createGroup updated to new callback format
+- Usergrid.Client.prototype.createEntity updated to new callback format
+- Usergrid.Client.prototype.getEntity updated to new callback format
+- Usergrid.Collection instantiation no longer fetches the collection automatically, this no longer takes a callback
+- Usergrid.Entity.prototype.save no longer handles password changes
+- Usergrid.Entity.prototype.changePassword added to handle password requests
+- Usergrid.Counter no longer needs a callback
+- Usergrid.Asset.prototype.download sets appropriate mime type for content
+- Usergrid.Asset.prototype.upload implemented retry interval to mitigate errors when an asset has not fully propagated
+ 
+ 
+###0.10.8
+- Added support for Events and Counters
+- Added support for Folders and Assets
+- Improved asynchronous call support
+- Improved callback handling
+- Numerous bug fixes
+
+###0.10.7
+- Bug fixes
+- most calls now return the raw data as the last parameter (called data)
+- added some management functions for authentication
+- added some methods to pull related data and append to an entity
+- helper method to remove an entity from a collection without hitting the database (you would use the .destroy method if you do want to hit the db)
+
+###0.10.4
+
+- Added new functions for creating, getting, and deleting connections
+- Added test cases for said functions
+- Added logout call to client create to clear out any remnant token from a past session
+- Fixed change password error
+- Added getEntity method to get existing entity from server
+
+###0.10.3
+
+- Added set / get token methods to accomodate session storage
+- Added createUserActivity method to make creating activities for logged in user easier
+
+###0.10.2
+
+- Removed local caching of user object in client
+
+###0.10.1
+
+- Complete refactor of the SDK to bring congruity with the App services Node module
+
+- Client object is now main entry point - all objects are created from the client, and all calls are run from the client
+
+- Removed Curl extension - now just use boolean in options object when creating client
+
+- Added full test coverage for all sample code in the readme file
+
+- Renamed SDK file to usergrid.js
+
+
+###0.9.10
+
+- Refactored directory structure.  SDK file now at root, extensions in their own directory, examples in their own directory.
+
+- Moved cURL command generator into a separate file (extensions/usergrid.curl.js).  Include this file after the the SDK if you want cURL command generation.
+
+- Moved Validation functionality into a separate file (extensions/usergrid.validation.js). Include this file after the the SDK if you want to use this functionality.
+
+- Moved Session file into a separate file (extensions/usergrid.session.js). Include this file after the the SDK if you want to use this functionality.
+
+- Removed deprecated get function from Collection object.
+
+- Added timeout callback.
+
+- Added beginnings of a qUnit test suite - only a few functions tested so far, but we will add to the suite as we progress.
+
+- Removed minified files.  We hope to host these files on a CDN soon.
diff --git a/examples/all-calls/all-calls.html b/examples/all-calls/all-calls.html
new file mode 100755
index 0000000..f4861dc
--- /dev/null
+++ b/examples/all-calls/all-calls.html
@@ -0,0 +1,176 @@
+<!--

+  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>All Calls Apigee App Services (Usergrid) Example</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="app.js" type="text/javascript"></script>

+   </head>

+   <body>

+      <div class="header">

+         <img src="../resources/images/apigee.png"> App Services (Usergrid) Javascript SDK Example

+      </div>

+      <div class="breadcrumb">

+        <a href="../../index.html">&lt;&lt; examples</a>

+      </div>

+      <div class="info">

+        This sample application is a quick example to show how to make App Services (Usergrid) calls - GET, POST, PUT, DELETE - with our Javascript SDK.

+        Also included is a login example. For more information on App Services, see our <a href="http://apigee.com/docs/app_services">docs</a>.

+      </div>

+      <div id="main" class="main">

+         <h3>Select method to test:</h3>

+         <ul class="nav nav-tabs">

+          <li id="get-nav" class="active" ><a id="show-get" href="javascript:void(0);">GET</a></li>

+          <li id="post-nav"><a id="show-post" href="javascript:void(0);">POST</a></li>

+          <li id="put-nav"><a id="show-put" href="javascript:void(0);">PUT</a></li>

+          <li id="delete-nav"><a id="show-delete" href="javascript:void(0);">DELETE</a></li>

+          <li id="login-nav"><a id="show-login" href="javascript:void(0);">LOG IN</a></li>

+        </ul>

+

+        <div id="get-page" class="form-block">

+           <div class="section-header">Method: <b>GET</b></div>

+           <div class="well">

+              <div id="name-control" class="control-group">

+                 <div class="controls">

+                    <label class="control-label" for="get-path">Path</label>

+                    <input type="text" name="get-path" id="get-path" class="span4" style="float: left;" value="users/fred"/>

+                    <span class="span4 left" style="width: 10px; float: left;">&nbsp;</span>

+                    <button class="btn btn-primary" id="run-get" style="width: 90px;">Run Query</button>

+                    <span style="clear: both;">&nbsp;</span>

+                 </div>

+              </div>

+           </div>

+           <p class="note">

+              To run a <b>GET</b> query against the API, enter the path you want to query, then push the "Run Query" button. By default, the value is "users/fred", which will translate to a call

+              to: https://api.usergrid.com/your-org/your-app/users/fred, and will retrieve the record for fred.

+              <br/><br/>

+              <b>Note:</b> If you get an error here, it probably means "fred" doesn't exist. Choose "POST" to create a new "fred".

+           </p>

+           <div class="section-header"><b>GET</b> API Response</div>

+

+        </div>

+

+        <div id="post-page" class="form-block">

+           <div class="section-header">Method: <b>POST</b></div>

+           <div class="well">

+              <div id="post-name-control" class="control-group">

+                 <div class="controls">

+                    <label class="control-label" for="post-path">Path</label>

+                    <input type="text" name="post-path" id="post-path" class="span4" value="users" />

+

+                    <label class="control-label" for="post-data" style="padding-top: 10px;">Json Request Body</label>

+                    <input type="text" name="post-data" id="post-data" class="span4" style="float: left" value='{"username":"fred", "password":"barney"}' />

+                    <span style="width: 10px; float: left;">&nbsp;</span>

+                    <button class="btn btn-primary" id="run-post" style="width: 90px;">Run Query</button>

+                    <span style="clear: both;">&nbsp;</span>

+                 </div>

+              </div>

+           </div>

+           <p class="note">

+              To run a <b>POST</b> query against the API, enter the path you want to call and the JSON you want to send to the server, then push the "Run Query" button. By default, the path

+              of users, and the JSON request body of, "{"username":"fred"}" will create a new user called "fred".

+              <br/><br/>

+              <b>Note:</b> If you get an error here, it probably means "fred" already exists. Choose "DELETE", run the DELETE query to delete the "fred" entity, then try the POST query again

+           </p>

+           <div class="section-header"><b>POST</b> API Response</div>

+        </div>

+

+        <div id="put-page" class="form-block">

+           <div class="section-header">Method: <b>PUT</b></div>

+           <div class="well">

+              <div id="put-name-control" class="control-group">

+                 <div class="controls">

+                    <label class="control-label" for="put-path">Path</label>

+                    <input type="text" name="put-path" id="put-path" class="span4" value="users/fred" />

+

+                    <label class="control-label" for="put-data" style="padding-top: 10px;">Json Request Body</label>

+                    <input type="text" name="put-data" id="put-data" class="span4" style="float: left" value='{"othervalue":"12345"}' />

+                    <span style="width: 10px; float: left;">&nbsp;</span>

+                    <button class="btn btn-primary" id="run-put" style="width: 90px;">Run Query</button>

+                    <span style="clear: both;">&nbsp;</span>

+                 </div>

+              </div>

+           </div>

+           <p class="note">

+              To run a <b>PUT</b> query against the API, enter the path you want to update and the JSON you want to send to the server, then push the "Run Query" button. By default, the

+              path of users/fred, and the JSON Request Body of "{"othervalue":"12345"}" will update the "fred" entity with the JSON Request Body.

+              <br/><br/>

+              <b>Note:</b> If you get an error here, it probably means "fred" doesn't exist. Choose "POST" to create a new "fred".

+           </p>

+           <div class="section-header"><b>PUT</b> API Response</div>

+        </div>

+

+        <div id="delete-page" class="form-block">

+           <div class="section-header">Method: <b>DELETE</b></div>

+           <div class="well">

+              <div id="delete-name-control" class="control-group">

+                 <div class="controls">

+                    <label class="control-label" for="delete-path">Path</label>

+                    <input type="text" name="delete-path" id="delete-path" class="span4" style="float: left;" value="users/fred"/>

+                    <span class="span4 left" style="width: 10px; float: left;">&nbsp;</span>

+                    <button class="btn btn-primary" id="run-delete" style="width: 90px;">Run Query</button>

+                    <span style="clear: both;">&nbsp;</span>

+                 </div>

+              </div>

+           </div>

+           <p class="note">

+              To run a <b>DELETE</b> query against the API, enter the path you want to update and the JSON you want to send to the server, then push the "Run Query" button. By default, the

+              path of users/fred, and the JSON Request Body of "{"othervalue":"12345"}" will update the "fred" entity with the JSON Request Body.

+              <br/><br/>

+              <b>Note:</b> If you get an error here, it probably means "fred" doesn't exist. Choose "POST" to create a new "fred".

+           </p>

+           <div class="section-header"><b>DELETE</b> API Response</div>

+        </div>

+

+        <div id="login-page" class="form-block">

+           <div class="section-header">Method: <b>Log In</b></div>

+           <div class="well">

+              <div id="login-name-control" class="control-group">

+                 <div class="controls">

+                    <label class="control-label" for="username">Username</label>

+                    <input type="text" name="username" id="username" class="span4" value="fred"/>

+

+                    <label class="control-label" for="password" style="padding-top: 10px;">Password</label>

+                    <!-- note: normally the password field would be of type password so the input is hidden.  For this demo

+                         we have left it in the clear so that it is easier to use.  Please use type password for production apps! -->

+                    <input type="text" name="password" id="password" class="span4" style="float: left" value="barney"/>

+                    <span style="width: 10px; float: left;">&nbsp;</span>

+                    <button class="btn btn-primary" id="run-login" style="width: 90px;">Run Query</button>

+                    <span style="clear: both;">&nbsp;</span>

+                 </div>

+              </div>

+           </div>

+           <p class="note">

+              To test a <b>login</b> against the API, enter the username and password, then push the "Run Query" button. By default, the username is set to "fred" and the password is set to

+              "barney", as created in the default "POST" method.

+              <br/><br/>

+              <b>Note:</b> If you get an error here, it probably means either the user doesn't exist, or hasn't had the password set properly. Choose "POST" to create a new "fred".

+           </p>

+           <div class="section-header"><b>Log In</b> API Response</div>

+        </div>

+

+        <div class="well" id="response">

+          // Press 'Run Query' to send the API call.

+       </div>

+      </div>

+    </body>

+</html>

diff --git a/examples/all-calls/app.js b/examples/all-calls/app.js
new file mode 100755
index 0000000..6bf804e
--- /dev/null
+++ b/examples/all-calls/app.js
@@ -0,0 +1,236 @@
+//
+// 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.
+//
+
+/*
+*  All Calls is a 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 app.js
+*  @author Rod Simpson (rod@apigee.com)
+*
+*  This file contains the main program logic for All Calls App.
+*/
+$(document).ready(function () {
+  //first set the org / app path (must be orgname / appname or org id / app id - can't mix names and uuids!!)
+
+  var client = new Usergrid.Client({
+    orgName:'yourorgname',
+    appName:'yourappname',
+    logging: true, //optional - turn on logging, off by default
+    buildCurl: true //optional - turn on curl commands, off by default
+  });
+
+  function hideAllSections(){
+    $('#get-page').hide();
+    $('#get-nav').removeClass('active');
+    $('#post-page').hide();
+    $('#post-nav').removeClass('active');
+    $('#put-page').hide();
+    $('#put-nav').removeClass('active');
+    $('#delete-page').hide();
+    $('#delete-nav').removeClass('active');
+    $('#login-page').hide();
+    $('#login-nav').removeClass('active');
+    $('#response').html("// Press 'Run Query' to send the API call.");
+  }
+  //bind the show buttons
+  $('#show-get').bind('click', function() {
+    hideAllSections();
+    $('#get-nav').addClass('active');
+    $('#get-page').show();
+  });
+
+  $('#show-post').bind('click', function() {
+    hideAllSections();
+    $('#post-nav').addClass('active');
+    $('#post-page').show();
+  });
+
+  $('#show-put').bind('click', function() {
+    hideAllSections();
+    $('#put-nav').addClass('active');
+    $('#put-page').show();
+  });
+
+  $('#show-delete').bind('click', function() {
+    hideAllSections();
+    $('#delete-nav').addClass('active');
+    $('#delete-page').show();
+  });
+
+  $('#show-login').bind('click', function() {
+    hideAllSections();
+    $('#login-nav').addClass('active');
+    $('#login-page').show();
+  });
+
+  $('#run-get').bind('click', function() {
+    _get();
+  });
+
+  $('#run-post').bind('click', function() {
+    _post();
+  });
+
+  $('#run-put').bind('click', function() {
+    _put();
+  });
+
+  $('#run-delete').bind('click', function() {
+    _delete();
+  });
+
+  $('#run-login').bind('click', function() {
+    _login();
+  });
+
+  //start with the get page showing by default
+  $('#get-page').show();
+
+  //bind the create new dog button
+  $('#main-menu').bind('click', function() {
+    $('#get-page').hide();
+    $('#post-page').hide();
+    $('#put-page').hide();
+    $('#delete-page').hide();
+    $('#login-page').hide();
+    $('#main').show();
+    $("#response").html('');
+  });
+
+  function _get() {
+    var endpoint = $("#get-path").val();
+
+    var options = {
+      method:'GET',
+      endpoint:endpoint
+    };
+    client.request(options, function (err, data) {
+      //data will contain raw results from API call
+      if (err) {
+        var output = JSON.stringify(data, null, 2);
+        $("#response").html('<pre>ERROR: '+output+'</pre>');
+      } else {
+        var output = JSON.stringify(data, null, 2);
+        $("#response").html('<pre>'+output+'</pre>');
+      }
+    });
+  }
+
+  function _post() {
+    var endpoint = $("#post-path").val();
+    var data = $("#post-data").val();
+    data = JSON.parse(data);
+
+    var options = {
+      method:'POST',
+      endpoint:endpoint,
+      body:data
+    };
+    client.request(options, function (err, data) {
+      //data will contain raw results from API call
+      if (err) {
+        var output = JSON.stringify(data, null, 2);
+        $("#response").html('<pre>ERROR: '+output+'</pre>');
+      } else {
+        var output = JSON.stringify(data, null, 2);
+        $("#response").html('<pre>'+output+'</pre>');
+      }
+    });
+  }
+
+  function _put() {
+    var endpoint = $("#put-path").val();
+    var data = $("#put-data").val();
+    data = JSON.parse(data);
+
+    var options = {
+      method:'PUT',
+      endpoint:endpoint,
+      body:data
+    };
+    client.request(options, function (err, data) {
+      //data will contain raw results from API call
+      if (err) {
+        var output = JSON.stringify(data, null, 2);
+        $("#response").html('<pre>ERROR: '+output+'</pre>');
+      } else {
+        var output = JSON.stringify(data, null, 2);
+        $("#response").html('<pre>'+output+'</pre>');
+      }
+    });
+  }
+
+  function _delete() {
+    var endpoint = $("#delete-path").val();
+
+    var options = {
+      method:'DELETE',
+      endpoint:endpoint
+    };
+    client.request(options, function (err, data) {
+      //data will contain raw results from API call
+      if (err) {
+        var output = JSON.stringify(data, null, 2);
+        $("#response").html('<pre>ERROR: '+output+'</pre>');
+      } else {
+        var output = JSON.stringify(data, null, 2);
+        $("#response").html('<pre>'+output+'</pre>');
+      }
+    });
+  }
+
+  function _login() {
+    var username = $("#username").val();
+    var password = $("#password").val();
+
+    client.login(username, password, function (err, data) {
+      //at this point, the user has been logged in succesfully and the OAuth token for the user has been stored
+      //however, in this example, we don't want to use that token for the rest of the API calls, so we will now
+      //reset it.  In your app, you will most likely not want to do this, as you are effectively logging the user
+      //out.  Our calls work because we are going against the Sandbox app, which has no restrictions on permissions.
+      client.token = null; //delete the user's token by setting it to null
+      if (err) {
+        var output = JSON.stringify(data, null, 2);
+        $("#response").html('<pre>ERROR: '+output+'</pre>');
+      } else {
+        var output = JSON.stringify(data, null, 2);
+        $("#response").html('<pre>'+output+'</pre>');
+      }
+    });
+  }
+
+});
\ No newline at end of file
diff --git a/examples/dogs/app.js b/examples/dogs/app.js
new file mode 100755
index 0000000..930c175
--- /dev/null
+++ b/examples/dogs/app.js
@@ -0,0 +1,194 @@
+//
+// 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.
+//
+
+/**
+*  dogs is a sample app  that is powered by Usergrid
+*  This app shows how to use the Usergrid SDK to connect
+*  to Usergrid, how to add entities, and how to page through
+*  a result set of entities
+*
+*  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 app.js
+*  @author Rod Simpson (rod@apigee.com)
+*
+*  This file contains the main program logic for Dogs.
+*/
+$(document).ready(function () {
+   //first set the org / app path (must be orgname / appname or org id / app id - can't mix names and uuids!!)
+   var client = new Usergrid.Client({
+    orgName:'yourorgname',
+    appName:'dogs',
+    logging: true, //optional - turn on logging, off by default
+    buildCurl: true //optional - turn on curl commands, off by default
+  });
+
+  //make a new "dogs" Collection
+  var options = {
+    type:'dogs',
+    qs:{ql:'order by created DESC'}
+  }
+
+  var dogs;
+
+  client.createCollection(options, function (err, response, collection) {
+    if (err) {
+      $('#mydoglist').html('could not load dogs');
+    } else {
+      dogs=collection;
+      //bind the next button to the proper method in the collection object
+      $('#next-button').bind('click', function() {
+        $('#message').html('');
+        dogs.getNextPage(function(err, data){
+          if (err) {
+            alert('could not get next page of dogs');
+          } else {
+            drawDogs();
+          }
+        });
+      });
+
+      //bind the previous button to the proper method in the collection object
+      $('#previous-button').bind('click', function() {
+        $('#message').html('');
+        dogs.getPreviousPage(function(err, data){
+          if (err) {
+            alert('could not get previous page of dogs');
+          } else {
+            drawDogs();
+          }
+        });
+      });
+
+      //bind the new button to show the "create new dog" form
+      $('#new-dog-button').bind('click', function() {
+         $('#dogs-list').hide();
+         $('#new-dog').show();
+      });
+
+      //bind the create new dog button
+      $('#create-dog').bind('click', function() {
+        newdog();
+      });
+
+      //bind the create new dog button
+      $('#cancel-create-dog').bind('click', function() {
+        $('#new-dog').hide();
+        $('#dogs-list').show();
+        drawDogs();
+      });
+
+      function drawDogs() {
+        dogs.fetch(function(err, data) {
+          if(err) {
+            alert('there was an error getting the dogs');
+          } else {
+            //first empty out all the current dogs in the list
+            $('#mydoglist').empty();
+            //then hide the next / previous buttons
+            $('#next-button').hide();
+            $('#previous-button').hide();
+            //iterate through all the items in this "page" of data
+            //make sure we reset the pointer so we start at the beginning
+            dogs.resetEntityPointer();
+            while(dogs.hasNextEntity()) {
+               //get a reference to the dog
+               var dog = dogs.getNextEntity();
+               //display the dog in the list
+               $('#mydoglist').append('<li>'+ dog.get('name') + '</li>');
+            }
+            //if there is more data, display a "next" button
+            if (dogs.hasNextPage()) {
+               //show the button
+               $('#next-button').show();
+            }
+            //if there are previous pages, show a "previous" button
+            if (dogs.hasPreviousPage()) {
+               //show the button
+               $('#previous-button').show();
+            }
+          }
+        });
+      }
+
+      function newdog() {
+        $('#create-dog').addClass("disabled");
+        //get the values from the form
+        var name = $("#name").val();
+
+        //make turn off all hints and errors
+        $("#name-help").hide();
+        $("#name-control").removeClass('error');
+
+        //make sure the input was valid
+        if (Usergrid.validation.validateName(name, function (){
+          $("#name").focus();
+          $("#name-help").show();
+          $("#name-control").addClass('error');
+          $("#name-help").html(Usergrid.validation.getNameAllowedChars());
+          $('#create-dog').removeClass("disabled");})
+        ) {
+
+          //all is well, so make the new dog
+          //create a new dog and add it to the collection
+          var options = {
+            name:name
+          }
+          //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) {
+              //let the user know there was a problem
+              alert('Oops! There was an error creating the dog.');
+              //enable the button so the form will be ready for next time
+              $('#create-dog').removeClass("disabled");
+            } else {
+              $('#message').html('New dog created!');
+              //the save worked, so hide the new dog form
+              $('#new-dog').hide();
+              //then show the dogs list
+              $('#dogs-list').show();
+              //then call the function to get the list again
+              drawDogs();
+              //finally enable the button so the form will be ready for next time
+              $('#create-dog').removeClass("disabled");
+            }
+          });
+        }
+      }
+      drawDogs();
+
+    }
+  });
+
+});
diff --git a/examples/dogs/dogs.html b/examples/dogs/dogs.html
new file mode 100755
index 0000000..8f92830
--- /dev/null
+++ b/examples/dogs/dogs.html
@@ -0,0 +1,74 @@
+<!--

+  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>Dogs Example App for Apigee App Services (Usergrid)</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="../../extensions/usergrid.validation.js" type="text/javascript"></script>

+      <script src="app.js" type="text/javascript"></script>

+   </head>

+   <body>

+      <div class="header">

+         <img src="../resources/images/apigee.png"> App Services (Usergrid) Javascript SDK

+      </div>

+      <div class="breadcrumb">

+        <a href="../../index.html">&lt;&lt; examples</a>

+      </div>

+      <div id="dogs-list" style="width: 600px; min-height: 450px; margin: auto; display: block; background-color: #fff; padding: 30px;">

+         <h2>Dogs</h2>

+         <div style="padding: 10px;">

+            Dogs is a simple application that shows how to create entities and collecitons, how to iterate through results, and how to

+            display larger result sets in a paged format.

+         </div>

+         <div style="height: 30px;">

+           <div style="float: left; padding-left: 10px; font-weight: 600;" id="message"> </div>

+           <button id="new-dog-button" style="float:right; width: 90px;" class="btn btn-primary" >New Dog</button>

+         </div>

+         <div style="height:300px;">

+            <ul id="mydoglist"><li>No dogs yet!</li></ul>

+         </div>

+         <button id="previous-button" style="float:left; display: none; width: 90px;" class="btn btn-primary" >Previous</button>

+         <button id="next-button" style="float:right; display: none; width: 90px;" class="btn btn-primary" >Next</button>

+         <div style="height: 30px;">&nbsp;</div>

+      </div>

+

+      <div id="new-dog" style="width: 600px; min-height: 450px; margin: auto; display: block; background-color: #fff; padding: 30px; display: none;">

+         <h2>Dogs</h2>

+         <div style="padding: 10px;">

+            Use this form to create a new dog.

+         </div>

+         <form name="new-dog-form" id="new-dog-form">

+            <div id="name-control" class="control-group">

+               <label class="control-label" for="name">Dog Name</label>

+               <div class="controls">

+                  <input type="text" name="name" id="name" class="span4" />

+                  <span id="name-help" class="help-inline" style="display: none">Please correct the error</span>

+               </div>

+            </div>

+         </form>

+         <button class="btn btn-primary" id="create-dog" style="width: 90px;">Create dog</button>

+         <button class="btn btn-primary" id="cancel-create-dog" style="width: 90px;">Cancel</button>

+      </div>

+

+    </body>

+</html>

diff --git a/examples/facebook/app.js b/examples/facebook/app.js
new file mode 100755
index 0000000..35b646d
--- /dev/null
+++ b/examples/facebook/app.js
@@ -0,0 +1,129 @@
+//
+// 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.
+//
+
+/**
+*  dogs is a sample app  that is powered by Usergrid
+*  This app shows how to use the Usergrid SDK to connect
+*  to Usergrid, how to add entities, and how to page through
+*  a result set of entities
+*
+*  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 app.js
+*  @author Rod Simpson (rod@apigee.com)
+*
+*  This file contains the main program logic for Facebook Login Example.
+*/
+$(document).ready(function () {
+  //first set the org / app path (must be orgname / appname or org id / app id - can't mix names and uuids!!)
+
+  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
+  });
+
+  //bind the login button so we can send the user to facebook
+  $('#login-button').bind('click', function() {
+    //get the
+    var apiKey = $("#api-key").val();
+    var location = window.location.protocol + '//' + window.location.host;
+    var path = window.location.pathname;
+
+    var link = "https://www.facebook.com/dialog/oauth?client_id=";
+    link += apiKey;
+    link += "&redirect_uri=";
+    link += location+path
+    link += "&scope&COMMA_SEPARATED_LIST_OF_PERMISSION_NAMES&response_type=token";
+
+    //now forward the user to facebook
+    window.location = link;
+  });
+  //bind the previous button to the proper method in the collection object
+  $('#logout-button').bind('click', function() {
+    logout();
+  });
+
+  //load up the facebook api sdk
+  window.fbAsyncInit = function() {
+    FB.init({
+      appId      : '308790195893570', // App ID
+      channelUrl : '//usergridsdk.dev//examples/channel.html', // Channel File
+      status     : true, // check login status
+      cookie     : true, // enable cookies to allow the server to access the session
+      xfbml      : true  // parse XFBML
+    });
+  };
+
+  function logout() {
+    FB.logout(function(response) {
+      client.logoutAppUser();
+      var html = "User logged out. \r\n\r\n // Press 'Log in' to log in with Facebook.";
+      $('#facebook-status').html(html);
+    });
+  }
+
+  function login(facebookAccessToken) {
+    client.loginFacebook(facebookAccessToken, function(err, response){
+      var output = JSON.stringify(response, null, 2);
+      if (err) {
+        var html = '<pre>Oops!  There was an error logging you in. \r\n\r\n';
+        html += 'Error: \r\n' + output+'</pre>';
+      } else {
+        var html = '<pre>Hurray!  You have been logged in. \r\n\r\n';
+        html += 'Facebook Token: ' + '\r\n' + facebookAccessToken + '\r\n\r\n';
+        html += 'Facebook Profile data stored in Usergrid: \r\n' + output+'</pre>';
+      }
+      $('#facebook-status').html(html);
+    })
+  }
+
+  //pull the access token out of the query string
+  var ql = [];
+  if (window.location.hash) {
+    // split up the query string and store in an associative array
+    var params = window.location.hash.slice(1).split("#");
+    var tmp = params[0].split("&");
+    for (var i = 0; i < tmp.length; i++) {
+      var vals = tmp[i].split("=");
+      ql[vals[0]] = unescape(vals[1]);
+    }
+  }
+
+  if (ql['access_token']) {
+    var facebookAccessToken = ql['access_token']
+    //try to log in with facebook
+    login(facebookAccessToken);
+  }
+});
\ No newline at end of file
diff --git a/examples/facebook/facebook.html b/examples/facebook/facebook.html
new file mode 100755
index 0000000..2ea8943
--- /dev/null
+++ b/examples/facebook/facebook.html
@@ -0,0 +1,73 @@
+<!--

+  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>Facebook Login Example App for Apigee App Services (Usergrid)</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="app.js" type="text/javascript"></script>

+    <script src="//connect.facebook.net/en_US/all.js" type="text/javascript"></script>

+  </head>

+  <body>

+    <div class="header">

+      <img src="../resources/images/apigee.png"> App Services (Usergrid) Javascript SDK

+    </div>

+    <div class="breadcrumb">

+        <a href="../../index.html">&lt;&lt; examples</a>

+      </div>

+    <div class="info">

+      This sample application will show you how to log into App Services (Usergrid) using Facebook and the Usergrid Javascript SDK.

+      Enter the <a href="https://developers.facebook.com/apps/">API Key that you get from Facebook</a>, then log in.

+      <br/><br/>

+      The Log in button sends the user to the facebook login page.  Once the user logs in, they are redirected back to

+      this page and automatically logged into Usergrid.  If the user is already logged into facebook, then they don't need to log in again.

+      <br/><br/>

+      Clicking the log out button calls the logout method of the Facebook JS SDK, and also logs the user out of Usergrid by calling the Usergrid logoutAppUser method.

+      <br/><br/>

+      For a step by step walk-thru on how to get this app running, see this <a href="guide.html">guide</a>.

+      <br/><br/>

+      For more information on App Services, see our <a href="http://apigee.com/docs/app_services">docs</a> site, specifically or our <a href="http://apigee.com/docs/usergrid/content/facebook-sign-in">Facebook docs page</a>.

+      <br/><br/>

+      For more information on how using the Facebook JS SDK to log users in, see this guide: <a href="http://developers.facebook.com/docs/howtos/login/getting-started/">http://developers.facebook.com/docs/howtos/login/getting-started/</a>

+    </div>

+    <div id="main" class="main">

+      <div class="section-header">Log in with Facebook</div>

+      <div class="well">

+        <div id="name-control" class="control-group">

+          <div class="controls">

+            <label class="control-label" for="get-path">Facebook API key (<a href="https://developers.facebook.com" target="_blank">Learn more</a>)</label>

+            <input type="text" name="api-key" id="api-key" class="span4" style="float: left;" value="308790195893570"/>

+            <span class="span4 left" style="width: 10px; float: left;">&nbsp;</span>

+            <button class="btn btn-primary" id="login-button" style="width: 90px;">Log In</button>

+            <button class="btn btn-primary" id="logout-button" style="width: 90px;">Log out</button>

+            <span style="clear: both;">&nbsp;</span>

+          </div>

+        </div>

+      </div>

+      <div class="section-header"><b>API Response</b></div>

+      <div class="well" id="facebook-status">

+        // Press 'Log in' to log in with Facebook.

+      </div>

+    </div>

+    <div id="fb-root" class=" fb_reset"> </div>

+  </body>

+</html>

diff --git a/examples/facebook/guide.html b/examples/facebook/guide.html
new file mode 100755
index 0000000..725dd9a
--- /dev/null
+++ b/examples/facebook/guide.html
@@ -0,0 +1,63 @@
+<!--

+  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="../resources/css/bootstrap-combined.min.css" />

+    <link rel="stylesheet" href="../resources/css/styles.css" />

+  </head>

+  <body>

+    <div class="header">

+      <img src="../resources/images/apigee.png"> App Services (Usergrid) Javascript SDK

+    </div>

+    <div class="info">

+      To get the Facebook example working on your development machine, follow these steps:

+      <br/><br/>

+      <b>1. Sign up</b>

+      <br/>Sign up for a <a href="http://facebook.com">Facebook</a> account.

+      <br/><br/>

+      <b>2. Create a new app</b>

+      <br/>Go to <a href="https://developers.facebook.com">https://developers.facebook.com</a> and follow

+      the prompts to authenticate yourself.  Then, go to the Facebook App Dashboard, and create a new app.

+      <br/><br/>

+      <b>3. Specify your Facebook integration method</b>

+      <br/>On the app creation page, make sure you specify that you will use "Website with Facebook Login".

+      You should see something like <a href="http://cl.ly/image/0K2L1z062Q1H">this</a>. You should enter the url where

+      you will host this sample (e.g. http://mywebsite.com/path-to-my-code). If want just to test with our gh-pages repo,

+      located here:  <a href="http://apigee.github.com/usergrid-javascript-sdk/">http://apigee.github.com/usergrid-javascript-sdk/</a>,

+      then enter http://apigee.github.com/usergrid-javascript-sdk in the field instead.

+      <br/><br/>

+      <b>4. Get your App Id</b>

+      <br/>Once your app is created, you can get the app id by going here:  <a href="https://developers.facebook.com/apps">https://developers.facebook.com/apps</a>

+      <br/><br/>

+      <b>5. Download this code</b> (skip this step if you plan to run it on our gh-pages repo)

+      <br/>If you haven't done so already, download the Usergrid Javascript SDK: <a href="https://github.com/apigee/usergrid-javascript-sdk">https://github.com/apigee/usergrid-javascript-sdk</a>

+      and save to a local directory on your hard drive. You will then need to deploy the code to your server.

+      <br/><br/>

+      <b>6. Run the file</b>

+      <br/>Navigate your browser to the location where you uploaded your code, or go to our gh-pages repo

+      ( <a href="http://apigee.github.com/usergrid-javascript-sdk/">http://apigee.github.com/usergrid-javascript-sdk/</a>) and choose "Facebook Login Example".

+      <br/><br/>

+      <b>7. Log in!</b>

+      <br/>Enter your API key, and follow the prompts.

+    </div>

+  </body>

+</html>

+

diff --git a/examples/persistence/test.html b/examples/persistence/test.html
new file mode 100644
index 0000000..3d7c553
--- /dev/null
+++ b/examples/persistence/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 to demonstrate how to persist collections using the collection.serialize() method.
+</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>
diff --git a/examples/persistence/test.js b/examples/persistence/test.js
new file mode 100644
index 0000000..352323a
--- /dev/null
+++ b/examples/persistence/test.js
@@ -0,0 +1,130 @@
+//
+// 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.
+//
+
+$(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 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
+  });
+
+  function runner(step, arg, arg2){
+    step++;
+    switch(step)
+    {
+      case 1:
+        notice('-----running step '+step+': create and serialize collection');
+        createAndSerialzeCollection(step);
+        break;
+      case 2:
+        notice('-----running step '+step+': de-serialize collection');
+        deserializeCollection(step);
+        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);
+    }
+  }
+
+
+  function createAndSerialzeCollection(step){
+    var options = {
+      type:'books',
+      qs:{ql:'order by name'}
+    }
+
+    client.createCollection(options, function (err, books) {
+      if (err) {
+        error('could not make collection');
+      } else {
+
+        //collection made, now serialize and store
+        localStorage.setItem('item', books.serialize());
+
+        success('new Collection created and data stored');
+
+        runner(step);
+
+      }
+    });
+  }
+
+
+  function deserializeCollection(step){
+    var books = client.restoreCollection(localStorage.getItem('item'));
+
+    while(books.hasNextEntity()) {
+      //get a reference to the book
+      book = books.getNextEntity();
+      var name = book.get('name');
+      notice('book is called ' + name);
+    }
+
+    success('looped through books');
+
+    runner(step);
+  }
+
+});
\ No newline at end of file
diff --git a/examples/resources/css/bootstrap-combined.min.css b/examples/resources/css/bootstrap-combined.min.css
new file mode 100755
index 0000000..e87551e
--- /dev/null
+++ b/examples/resources/css/bootstrap-combined.min.css
@@ -0,0 +1,18 @@
+/*!
+ * Bootstrap v2.0.4
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:20px}.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.127659574%;*margin-left:2.0744680846382977%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:99.99999998999999%;*width:99.94680850063828%}.row-fluid .span11{width:91.489361693%;*width:91.4361702036383%}.row-fluid .span10{width:82.97872339599999%;*width:82.92553190663828%}.row-fluid .span9{width:74.468085099%;*width:74.4148936096383%}.row-fluid .span8{width:65.95744680199999%;*width:65.90425531263828%}.row-fluid .span7{width:57.446808505%;*width:57.3936170156383%}.row-fluid .span6{width:48.93617020799999%;*width:48.88297871863829%}.row-fluid .span5{width:40.425531911%;*width:40.3723404216383%}.row-fluid .span4{width:31.914893614%;*width:31.8617021246383%}.row-fluid .span3{width:23.404255317%;*width:23.3510638276383%}.row-fluid .span2{width:14.89361702%;*width:14.8404255306383%}.row-fluid .span1{width:6.382978723%;*width:6.329787233638298%}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;content:""}.container-fluid:after{clear:both}p{margin:0 0 9px}p small{font-size:11px;color:#999}.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px}h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999}h1{font-size:30px;line-height:36px}h1 small{font-size:18px}h2{font-size:24px;line-height:36px}h2 small{font-size:18px}h3{font-size:18px;line-height:27px}h3 small{font-size:14px}h4,h5,h6{line-height:18px}h4{font-size:14px}h4 small{font-size:12px}h5{font-size:12px}h6{font-size:11px;color:#999;text-transform:uppercase}.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eee}.page-header h1{line-height:1}ul,ol{padding:0;margin:0 0 9px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}ul{list-style:disc}ol{list-style:decimal}li{line-height:18px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}dl{margin-bottom:18px}dt,dd{line-height:18px}dt{font-weight:bold;line-height:17px}dd{margin-left:9px}.dl-horizontal dt{float:left;width:120px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:130px}hr{margin:18px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}strong{font-weight:bold}em{font-style:italic}.muted{color:#999}abbr[title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px}blockquote small{display:block;line-height:18px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:18px;font-style:normal;line-height:18px}small{font-size:100%}cite{font-style:normal}code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:18px}pre code{padding:0;color:inherit;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 18px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:13.5px;color:#999}label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555}input,textarea{width:210px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-ms-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer}input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}.uneditable-textarea{width:auto;height:auto}select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px}select{width:220px;border:1px solid #bbb}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.radio,.checkbox{min-height:18px;padding-left:18px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:930px}input.span11,textarea.span11,.uneditable-input.span11{width:850px}input.span10,textarea.span10,.uneditable-input.span10{width:770px}input.span9,textarea.span9,.uneditable-input.span9{width:690px}input.span8,textarea.span8,.uneditable-input.span8{width:610px}input.span7,textarea.span7,.uneditable-input.span7{width:530px}input.span6,textarea.span6,.uneditable-input.span6{width:450px}input.span5,textarea.span5,.uneditable-input.span5{width:370px}input.span4,textarea.span4,.uneditable-input.span4{width:290px}input.span3,textarea.span3,.uneditable-input.span3{width:210px}input.span2,textarea.span2,.uneditable-input.span2{width:130px}input.span1,textarea.span1,.uneditable-input.span1{width:50px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee;border-color:#ddd}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853}.control-group.warning .checkbox:focus,.control-group.warning .radio:focus,.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48}.control-group.error .checkbox:focus,.control-group.error .radio:focus,.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847}.control-group.success .checkbox:focus,.control-group.success .radio:focus,.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;content:""}.form-actions:after{clear:both}.uneditable-input{overflow:hidden;white-space:nowrap;cursor:not-allowed;background-color:#fff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}:-moz-placeholder{color:#999}:-ms-input-placeholder{color:#999}::-webkit-input-placeholder{color:#999}.help-block,.help-inline{color:#555}.help-block{display:block;margin-bottom:9px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-prepend,.input-append{margin-bottom:5px}.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:middle;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{z-index:2}.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc}.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;height:18px;min-width:16px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #fff;vertical-align:middle;background-color:#eee;border:1px solid #ccc}.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append .uneditable-input{border-right-color:#ccc;border-left-color:#eee}.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:9px}legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:18px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:160px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:160px}.form-horizontal .help-block{margin-top:9px;margin-bottom:0}.form-horizontal .form-actions{padding-left:160px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:18px}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9}.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5}table .span1{float:none;width:44px;margin-left:0}table .span2{float:none;width:124px;margin-left:0}table .span3{float:none;width:204px;margin-left:0}table .span4{float:none;width:284px;margin-left:0}table .span5{float:none;width:364px;margin-left:0}table .span6{float:none;width:444px;margin-left:0}table .span7{float:none;width:524px;margin-left:0}table .span8{float:none;width:604px;margin-left:0}table .span9{float:none;width:684px;margin-left:0}table .span10{float:none;width:764px;margin-left:0}table .span11{float:none;width:844px;margin-left:0}table .span12{float:none;width:924px;margin-left:0}table .span13{float:none;width:1004px;margin-left:0}table .span14{float:none;width:1084px;margin-left:0}table .span15{float:none;width:1164px;margin-left:0}table .span16{float:none;width:1244px;margin-left:0}table .span17{float:none;width:1324px;margin-left:0}table .span18{float:none;width:1404px;margin-left:0}table .span19{float:none;width:1484px;margin-left:0}table .span20{float:none;width:1564px;margin-left:0}table .span21{float:none;width:1644px;margin-left:0}table .span22{float:none;width:1724px;margin-left:0}table .span23{float:none;width:1804px;margin-left:0}table .span24{float:none;width:1884px;margin-left:0}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0}.icon-white{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";opacity:.3;filter:alpha(opacity=30)}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown:hover .caret,.open .caret{opacity:1;filter:alpha(opacity=100)}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:4px 0;margin:1px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#fff;text-decoration:none;background-color:#08c}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:"\2191"}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0,0,0,0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-ms-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-ms-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 10px 4px;margin-bottom:0;*margin-left:.3em;font-size:13px;line-height:18px;*line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff',endColorstr='#e6e6e6',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-ms-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.btn-large [class^="icon-"]{margin-top:1px}.btn-small{padding:5px 9px;font-size:11px;line-height:16px}.btn-small [class^="icon-"]{margin-top:-1px}.btn-mini{padding:2px 6px;font-size:11px;line-height:14px}.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#ccc;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.btn-primary{background-color:#0074cc;*background-color:#05c;background-image:-ms-linear-gradient(top,#08c,#05c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#05c));background-image:-webkit-linear-gradient(top,#08c,#05c);background-image:-o-linear-gradient(top,#08c,#05c);background-image:-moz-linear-gradient(top,#08c,#05c);background-image:linear-gradient(top,#08c,#05c);background-repeat:repeat-x;border-color:#05c #05c #003580;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc',endColorstr='#0055cc',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:#05c;*background-color:#004ab3}.btn-primary:active,.btn-primary.active{background-color:#004099 \9}.btn-warning{background-color:#faa732;*background-color:#f89406;background-image:-ms-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450',endColorstr='#f89406',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{background-color:#da4f49;*background-color:#bd362f;background-image:-ms-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(top,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#bd362f',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{background-color:#5bb75b;*background-color:#51a351;background-image:-ms-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(top,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462',endColorstr='#51a351',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{background-color:#49afcd;*background-color:#2f96b4;background-image:-ms-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(top,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de',endColorstr='#2f96b4',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{background-color:#414141;*background-color:#222;background-image:-ms-linear-gradient(top,#555,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#555),to(#222));background-image:-webkit-linear-gradient(top,#555,#222);background-image:-o-linear-gradient(top,#555,#222);background-image:-moz-linear-gradient(top,#555,#222);background-image:linear-gradient(top,#555,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#555555',endColorstr='#222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-group{position:relative;*margin-left:.3em;*zoom:1}.btn-group:before,.btn-group:after{display:table;content:""}.btn-group:after{clear:both}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:9px;margin-bottom:9px}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1}.btn-group>.btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.dropdown-toggle{*padding-top:4px;padding-right:8px;*padding-bottom:4px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini.dropdown-toggle{padding-right:5px;padding-left:5px}.btn-group>.btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px}.btn-group>.btn-large.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#05c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:7px;margin-left:0}.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100)}.btn-mini .caret{margin-top:5px}.btn-small .caret{margin-top:6px}.btn-large .caret{margin-top:6px;border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-top:0;border-bottom:5px solid #000}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:.75;filter:alpha(opacity=75)}.alert{padding:8px 35px 8px 14px;margin-bottom:18px;color:#c09853;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert-heading{color:inherit}.alert .close{position:relative;top:-2px;right:-21px;line-height:18px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:18px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>.pull-right{float:right}.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px}.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333;border-bottom-color:#333}.nav>.dropdown.active>a:hover{color:#000;cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.navbar{*position:relative;*z-index:2;margin-bottom:18px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top,#333,#222);background-image:-ms-linear-gradient(top,#333,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#222));background-image:-webkit-linear-gradient(top,#333,#222);background-image:-o-linear-gradient(top,#333,#222);background-image:linear-gradient(top,#333,#222);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333',endColorstr='#222222',GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1)}.navbar .container{width:auto}.nav-collapse.collapse{height:auto}.navbar{color:#999}.navbar .brand:hover{text-decoration:none}.navbar .brand{display:block;float:left;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#999}.navbar .navbar-text{margin-bottom:0;line-height:40px}.navbar .navbar-link{color:#999}.navbar .navbar-link:hover{color:#fff}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn{margin:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#fff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none}.navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right}.navbar .nav>li{display:block;float:left}.navbar .nav>li>a{float:none;padding:9px 10px 11px;line-height:19px;color:#999;text-decoration:none;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar .btn{display:inline-block;padding:4px 10px 4px;margin:5px 5px 6px;line-height:18px}.navbar .btn-group{padding:5px 5px 6px;margin:0}.navbar .nav>li>a:hover{color:#fff;text-decoration:none;background-color:transparent}.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#fff;text-decoration:none;background-color:#222}.navbar .divider-vertical{width:1px;height:40px;margin:0 9px;overflow:hidden;background-color:#222;border-right:1px solid #333}.navbar .nav.pull-right{margin-right:0;margin-left:10px}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;background-color:#2c2c2c;*background-color:#222;background-image:-ms-linear-gradient(top,#333,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#222));background-image:-webkit-linear-gradient(top,#333,#222);background-image:-o-linear-gradient(top,#333,#222);background-image:linear-gradient(top,#333,#222);background-image:-moz-linear-gradient(top,#333,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333',endColorstr='#222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{background-color:#222;*background-color:#151515}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#080808 \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown .dropdown-toggle .caret,.navbar .nav li.dropdown.open .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar .nav li.dropdown.active .caret{opacity:1;filter:alpha(opacity=100)}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:transparent}.navbar .nav li.dropdown.active>.dropdown-toggle:hover{color:#fff}.navbar .pull-right .dropdown-menu,.navbar .dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right .dropdown-menu:before,.navbar .dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right .dropdown-menu:after,.navbar .dropdown-menu.pull-right:after{right:13px;left:auto}.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top,#fff,#f5f5f5);background-image:-ms-linear-gradient(top,#fff,#f5f5f5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#fff,#f5f5f5);background-image:-o-linear-gradient(top,#fff,#f5f5f5);background-image:linear-gradient(top,#fff,#f5f5f5);background-repeat:repeat-x;border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff',endColorstr='#f5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.breadcrumb li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb .divider{padding:0 5px;color:#999}.breadcrumb .active a{color:#333}.pagination{height:36px;margin:18px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination li{display:inline}.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0}.pagination a:hover,.pagination .active a{background-color:#f5f5f5}.pagination .active a{color:#999;cursor:default}.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999;cursor:default;background-color:transparent}.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pager{margin-bottom:18px;margin-left:0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;content:""}.pager:after{clear:both}.pager li{display:inline}.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next a{float:right}.pager .previous a{float:left}.pager .disabled a,.pager .disabled a:hover{color:#999;cursor:default;background-color:#fff}.modal-open .dropdown-menu{z-index:2050}.modal-open .dropdown.open{*z-index:2050}.modal-open .popover{z-index:2060}.modal-open .tooltip{z-index:2070}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:50%;left:50%;z-index:1050;width:560px;margin:-250px 0 0 -280px;overflow:auto;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-ms-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:50%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-body{max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.tooltip{position:absolute;z-index:1020;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-2px}.tooltip.right{margin-left:2px}.tooltip.bottom{margin-top:2px}.tooltip.left{margin-left:-2px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px}.popover.top{margin-top:-5px}.popover.right{margin-left:5px}.popover.bottom{margin-top:5px}.popover.left{margin-left:-5px}.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.popover .arrow{position:absolute;width:0;height:0}.popover-inner{width:280px;padding:3px;overflow:hidden;background:#000;background:rgba(0,0,0,0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3)}.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.popover-content{padding:14px;background-color:#fff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:18px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:0 1px 1px rgba(0,0,0,0.075);box-shadow:0 1px 1px rgba(0,0,0,0.075)}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px}.label,.badge{font-size:10.998px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:18px;margin-bottom:18px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-ms-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(top,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5',endColorstr='#f9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{width:0;height:18px;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(top,#149bdf,#0480be);background-image:-ms-linear-gradient(top,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#149bdf',endColorstr='#0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-ms-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .bar{background-color:#149bdf;background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-ms-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(top,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35',GradientType=0)}.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-ms-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(top,#62c462,#57a957);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462',endColorstr='#57a957',GradientType=0)}.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-ms-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(top,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de',endColorstr='#339bb9',GradientType=0)}.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-ms-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450',endColorstr='#f89406',GradientType=0)}.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:18px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:18px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel .item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-ms-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel .item>img{display:block;line-height:1}.carousel .active,.carousel .next,.carousel .prev{display:block}.carousel .active{left:0}.carousel .next,.carousel .prev{position:absolute;top:0;width:100%}.carousel .next{left:100%}.carousel .prev{left:-100%}.carousel .next.left,.carousel .prev.right{left:0}.carousel .active.left{left:-100%}.carousel .active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:10px 15px 5px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{color:#fff}.hero-unit{padding:60px;margin-bottom:30px;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}
+/*!
+ * Bootstrap Responsive v2.0.4
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}@media(max-width:767px){.visible-phone{display:inherit!important}.hidden-phone{display:none!important}.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}}@media(min-width:768px) and (max-width:979px){.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:18px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.modal{position:absolute;top:10px;right:10px;left:10px;width:auto;margin:0}.modal.fade.in{top:auto}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:auto;margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:20px}.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.762430939%;*margin-left:2.709239449638298%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:99.999999993%;*width:99.9468085036383%}.row-fluid .span11{width:91.436464082%;*width:91.38327259263829%}.row-fluid .span10{width:82.87292817100001%;*width:82.8197366816383%}.row-fluid .span9{width:74.30939226%;*width:74.25620077063829%}.row-fluid .span8{width:65.74585634900001%;*width:65.6926648596383%}.row-fluid .span7{width:57.182320438000005%;*width:57.129128948638304%}.row-fluid .span6{width:48.618784527%;*width:48.5655930376383%}.row-fluid .span5{width:40.055248616%;*width:40.0020571266383%}.row-fluid .span4{width:31.491712705%;*width:31.4385212156383%}.row-fluid .span3{width:22.928176794%;*width:22.874985304638297%}.row-fluid .span2{width:14.364640883%;*width:14.311449393638298%}.row-fluid .span1{width:5.801104972%;*width:5.747913482638298%}input,textarea,.uneditable-input{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:714px}input.span11,textarea.span11,.uneditable-input.span11{width:652px}input.span10,textarea.span10,.uneditable-input.span10{width:590px}input.span9,textarea.span9,.uneditable-input.span9{width:528px}input.span8,textarea.span8,.uneditable-input.span8{width:466px}input.span7,textarea.span7,.uneditable-input.span7{width:404px}input.span6,textarea.span6,.uneditable-input.span6{width:342px}input.span5,textarea.span5,.uneditable-input.span5{width:280px}input.span4,textarea.span4,.uneditable-input.span4{width:218px}input.span3,textarea.span3,.uneditable-input.span3{width:156px}input.span2,textarea.span2,.uneditable-input.span2{width:94px}input.span1,textarea.span1,.uneditable-input.span1{width:32px}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:30px}.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.564102564%;*margin-left:2.510911074638298%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145300001%;*width:91.3997999636383%}.row-fluid .span10{width:82.905982906%;*width:82.8527914166383%}.row-fluid .span9{width:74.358974359%;*width:74.30578286963829%}.row-fluid .span8{width:65.81196581200001%;*width:65.7587743226383%}.row-fluid .span7{width:57.264957265%;*width:57.2117657756383%}.row-fluid .span6{width:48.717948718%;*width:48.6647572286383%}.row-fluid .span5{width:40.170940171000005%;*width:40.117748681638304%}.row-fluid .span4{width:31.623931624%;*width:31.5707401346383%}.row-fluid .span3{width:23.076923077%;*width:23.0237315876383%}.row-fluid .span2{width:14.529914530000001%;*width:14.4767230406383%}.row-fluid .span1{width:5.982905983%;*width:5.929714493638298%}input,textarea,.uneditable-input{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:1160px}input.span11,textarea.span11,.uneditable-input.span11{width:1060px}input.span10,textarea.span10,.uneditable-input.span10{width:960px}input.span9,textarea.span9,.uneditable-input.span9{width:860px}input.span8,textarea.span8,.uneditable-input.span8{width:760px}input.span7,textarea.span7,.uneditable-input.span7{width:660px}input.span6,textarea.span6,.uneditable-input.span6{width:560px}input.span5,textarea.span5,.uneditable-input.span5{width:460px}input.span4,textarea.span4,.uneditable-input.span4{width:360px}input.span3,textarea.span3,.uneditable-input.span3{width:260px}input.span2,textarea.span2,.uneditable-input.span2{width:160px}input.span1,textarea.span1,.uneditable-input.span1{width:60px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:18px}.navbar-fixed-bottom{margin-top:18px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 9px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#999;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#222}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:block;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222;border-bottom:1px solid #222;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
\ No newline at end of file
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
diff --git a/examples/resources/images/apigee.png b/examples/resources/images/apigee.png
new file mode 100755
index 0000000..c0d0f84
--- /dev/null
+++ b/examples/resources/images/apigee.png
Binary files differ
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 '&nbsp;'),
+                        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');
+        };
+    }
+}());
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>

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);
+      }
+    });
+  }
+});
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
+  }
+})();
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>
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));
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));
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
diff --git a/lib/modules/Client.js b/lib/modules/Client.js
new file mode 100644
index 0000000..e27ae65
--- /dev/null
+++ b/lib/modules/Client.js
@@ -0,0 +1,883 @@
+/*
+ *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.
+ */
+(function() {
+  var name = 'Client',
+    global = this,
+    overwrittenName = global[name],
+    exports;
+  var AUTH_ERRORS = [
+    "auth_expired_session_token",
+    "auth_missing_credentials",
+    "auth_unverified_oath",
+    "expired_token",
+    "unauthorized",
+    "auth_invalid"
+  ];
+  Usergrid.Client = function(options) {
+    //usergrid endpoint
+    this.URI = options.URI || 'https://api.usergrid.com';
+
+    //Find your Orgname and Appname in the Admin portal (http://apigee.com/usergrid)
+    if (options.orgName) {
+      this.set('orgName', options.orgName);
+    }
+    if (options.appName) {
+      this.set('appName', options.appName);
+    }
+    if (options.qs) {
+      this.setObject('default_qs', options.qs);
+    }
+    //other options
+    this.buildCurl = options.buildCurl || false;
+    this.logging = options.logging || false;
+
+    //timeout and callbacks
+    // this.logoutCallback =  options.logoutCallback || null;
+  };
+
+  /*
+   *  Main function for making requests to the API.  Can be called directly.
+   *
+   *  options object:
+   *  `method` - http method (GET, POST, PUT, or DELETE), defaults to GET
+   *  `qs` - object containing querystring values to be appended to the uri
+   *  `body` - object containing entity body for POST and PUT requests
+   *  `endpoint` - API endpoint, for example 'users/fred'
+   *  `mQuery` - boolean, set to true if running management query, defaults to false
+   *
+   *  @method request
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.request = function(options, callback) {
+    var method = options.method || 'GET';
+    var endpoint = options.endpoint;
+    var body = options.body || {};
+    var qs = options.qs || {};
+    var mQuery = options.mQuery || false; //is this a query to the management endpoint?
+    var orgName = this.get('orgName');
+    var appName = this.get('appName');
+    var default_qs = this.getObject('default_qs');
+    var uri;
+    /*var logoutCallback=function(){
+        if (typeof(this.logoutCallback) === 'function') {
+            return this.logoutCallback(true, 'no_org_or_app_name_specified');
+        }
+    }.bind(this);*/
+    if (!mQuery && !orgName && !appName) {
+      return logoutCallback();
+    }
+    if (mQuery) {
+      uri = this.URI + '/' + endpoint;
+    } else {
+      uri = this.URI + '/' + orgName + '/' + appName + '/' + endpoint;
+    }
+    if (this.getToken()) {
+      qs.access_token = this.getToken();
+      /*
+       **NOTE** The token can also be passed as a header on the request
+
+       xhr.setRequestHeader("Authorization", "Bearer " + self.getToken());
+       xhr.withCredentials = true;
+       */
+    }
+    if (default_qs) {
+      qs = propCopy(qs, default_qs);
+    }
+    var self=this;
+    var req = new Usergrid.Request(method, uri, qs, body, function(err, response) {
+      /*if (AUTH_ERRORS.indexOf(response.error) !== -1) {
+            return logoutCallback();
+        }*/
+      if(err){
+        doCallback(callback, [err, response, self], self);
+      }else{
+        doCallback(callback, [null, response, self], self);
+      }
+      //p.done(err, response);
+    });
+  };
+
+  /*
+   *  function for building asset urls
+   *
+   *  @method buildAssetURL
+   *  @public
+   *  @params {string} uuid
+   *  @return {string} assetURL
+   */
+  Usergrid.Client.prototype.buildAssetURL = function(uuid) {
+    var self = this;
+    var qs = {};
+    var assetURL = this.URI + '/' + this.orgName + '/' + this.appName + '/assets/' + uuid + '/data';
+
+    if (self.getToken()) {
+      qs.access_token = self.getToken();
+    }
+
+    //append params to the path
+    var encoded_params = encodeParams(qs);
+    if (encoded_params) {
+      assetURL += "?" + encoded_params;
+    }
+
+    return assetURL;
+  };
+
+  /*
+   *  Main function for creating new groups. Call this directly.
+   *
+   *  @method createGroup
+   *  @public
+   *  @params {string} path
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.createGroup = function(options, callback) {
+    var group = new Usergrid.Group({
+      path: options.path,
+      client: this,
+      data: options
+    });
+    group.save(function(err, response) {
+      doCallback(callback, [err, response, group], group);
+    });
+  };
+
+  /*
+   *  Main function for creating new entities - should be called directly.
+   *
+   *  options object: options {data:{'type':'collection_type', 'key':'value'}, uuid:uuid}}
+   *
+   *  @method createEntity
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.createEntity = function(options, callback) {
+    var entity = new Usergrid.Entity({
+      client: this,
+      data: options
+    });
+    entity.save(function(err, response) {
+      doCallback(callback, [err, response, entity], entity);
+    });
+  };
+  /*
+   *  Main function for getting existing entities - should be called directly.
+   *
+   *  You must supply a uuid or (username or name). Username only applies to users.
+   *  Name applies to all custom entities
+   *
+   *  options object: options {data:{'type':'collection_type', 'name':'value', 'username':'value'}, uuid:uuid}}
+   *
+   *  @method createEntity
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.getEntity = function(options, callback) {
+    var entity = new Usergrid.Entity({
+      client: this,
+      data: options
+    });
+    entity.fetch(function(err, response) {
+      doCallback(callback, [err, response, entity], entity);
+    });
+  };
+
+  /*
+   *  Main function for restoring an entity from serialized data.
+   *
+   *  serializedObject should have come from entityObject.serialize();
+   *
+   *  @method restoreEntity
+   *  @public
+   *  @param {string} serializedObject
+   *  @return {object} Entity Object
+   */
+  Usergrid.Client.prototype.restoreEntity = function(serializedObject) {
+    var data = JSON.parse(serializedObject);
+    var options = {
+      client: this,
+      data: data
+    };
+    var entity = new Usergrid.Entity(options);
+    return entity;
+  };
+  /*
+   *  Main function for creating new counters - should be called directly.
+   *
+   *  options object: options {timestamp:0, category:'value', counters:{name : value}}
+   *
+   *  @method createCounter
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, response, counter)
+   */
+  Usergrid.Client.prototype.createCounter = function(options, callback) {
+    var counter = new Usergrid.Counter({
+      client: this,
+      data: options
+    });
+    counter.save(callback);
+  };
+  /*
+   *  Main function for creating new assets - should be called directly.
+   *
+   *  options object: options {name:"photo.jpg", path:"/user/uploads", "content-type":"image/jpeg", owner:"F01DE600-0000-0000-0000-000000000000", file: FileOrBlobObject }
+   *
+   *  @method createCounter
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, response, counter)
+   */
+  Usergrid.Client.prototype.createAsset = function(options, callback) {
+    var file=options.file;
+    if(file){
+      options.name=options.name||file.name;
+      options['content-type']=options['content-type']||file.type;
+      options.path=options.path||'/';
+      delete options.file;
+    }
+    var asset = new Usergrid.Asset({
+      client: this,
+      data: options
+    });
+    asset.save(function(err, response, asset){
+      if(file && !err){
+        asset.upload(file, callback);
+      }else{
+        doCallback(callback, [err, response, asset], asset);
+      }
+    });
+  };
+
+  /*
+   *  Main function for creating new collections - should be called directly.
+   *
+   *  options object: options {client:client, type: type, qs:qs}
+   *
+   *  @method createCollection
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.createCollection = function (options, callback) {
+    options.client = this;
+    return new Usergrid.Collection(options, function(err, data, collection) {
+        console.log("createCollection", arguments);
+        doCallback(callback, [err, collection, data]);
+    });
+  };
+
+  /*
+   *  Main function for restoring a collection from serialized data.
+   *
+   *  serializedObject should have come from collectionObject.serialize();
+   *
+   *  @method restoreCollection
+   *  @public
+   *  @param {string} serializedObject
+   *  @return {object} Collection Object
+   */
+  Usergrid.Client.prototype.restoreCollection = function(serializedObject) {
+    var data = JSON.parse(serializedObject);
+    data.client = this;
+    var collection = new Usergrid.Collection(data);
+    return collection;
+  };
+
+  /*
+   *  Main function for retrieving a user's activity feed.
+   *
+   *  @method getFeedForUser
+   *  @public
+   *  @params {string} username
+   *  @param {function} callback
+   *  @return {callback} callback(err, data, activities)
+   */
+  Usergrid.Client.prototype.getFeedForUser = function(username, callback) {
+    var options = {
+      method: "GET",
+      endpoint: "users/" + username + "/feed"
+    };
+
+    this.request(options, function(err, data) {
+      if (err) {
+        doCallback(callback, [err]);
+      } else {
+        doCallback(callback, [err, data, data.getEntities()]);
+      }
+    });
+  };
+
+  /*
+   *  Function for creating new activities for the current user - should be called directly.
+   *
+   *  //user can be any of the following: "me", a uuid, a username
+   *  Note: the "me" alias will reference the currently logged in user (e.g. 'users/me/activties')
+   *
+   *  //build a json object that looks like this:
+   *  var options =
+   *  {
+   *    "actor" : {
+   *      "displayName" :"myusername",
+   *      "uuid" : "myuserid",
+   *      "username" : "myusername",
+   *      "email" : "myemail",
+   *      "picture": "http://path/to/picture",
+   *      "image" : {
+   *          "duration" : 0,
+   *          "height" : 80,
+   *          "url" : "http://www.gravatar.com/avatar/",
+   *          "width" : 80
+   *      },
+   *    },
+   *    "verb" : "post",
+   *    "content" : "My cool message",
+   *    "lat" : 48.856614,
+   *    "lon" : 2.352222
+   *  }
+   *
+   *  @method createEntity
+   *  @public
+   *  @params {string} user // "me", a uuid, or a username
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.createUserActivity = function(user, options, callback) {
+    options.type = 'users/' + user + '/activities';
+    options = {
+      client: this,
+      data: options
+    };
+    var entity = new Usergrid.Entity(options);
+    entity.save(function(err, data) {
+      doCallback(callback, [err, data, entity]);
+    });
+  };
+
+  /*
+   *  Function for creating user activities with an associated user entity.
+   *
+   *  user object:
+   *  The user object passed into this function is an instance of Usergrid.Entity.
+   *
+   *  @method createUserActivityWithEntity
+   *  @public
+   *  @params {object} user
+   *  @params {string} content
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.createUserActivityWithEntity = function(user, content, callback) {
+    var username = user.get("username");
+    var options = {
+      actor: {
+        "displayName": username,
+        "uuid": user.get("uuid"),
+        "username": username,
+        "email": user.get("email"),
+        "picture": user.get("picture"),
+        "image": {
+          "duration": 0,
+          "height": 80,
+          "url": user.get("picture"),
+          "width": 80
+        }
+      },
+      "verb": "post",
+      "content": content
+    };
+
+    this.createUserActivity(username, options, callback);
+
+  };
+
+  /*
+   *  A private method to get call timing of last call
+   */
+  Usergrid.Client.prototype.calcTimeDiff = function() {
+    var seconds = 0;
+    var time = this._end - this._start;
+    try {
+      seconds = ((time / 10) / 60).toFixed(2);
+    } catch (e) {
+      return 0;
+    }
+    return seconds;
+  };
+
+  /*
+   *  A public method to store the OAuth token for later use - uses localstorage if available
+   *
+   *  @method setToken
+   *  @public
+   *  @params {string} token
+   *  @return none
+   */
+  Usergrid.Client.prototype.setToken = function(token) {
+    this.set('token', token);
+  };
+
+  /*
+   *  A public method to get the OAuth token
+   *
+   *  @method getToken
+   *  @public
+   *  @return {string} token
+   */
+  Usergrid.Client.prototype.getToken = function() {
+    return this.get('token');
+  };
+
+  Usergrid.Client.prototype.setObject = function(key, value) {
+    if (value) {
+      value = JSON.stringify(value);
+    }
+    this.set(key, value);
+  };
+
+  Usergrid.Client.prototype.set = function(key, value) {
+    var keyStore = 'apigee_' + key;
+    this[key] = value;
+    if (typeof(Storage) !== "undefined") {
+      if (value) {
+        localStorage.setItem(keyStore, value);
+      } else {
+        localStorage.removeItem(keyStore);
+      }
+    }
+  };
+
+  Usergrid.Client.prototype.getObject = function(key) {
+    return JSON.parse(this.get(key));
+  };
+
+  Usergrid.Client.prototype.get = function(key) {
+    var keyStore = 'apigee_' + key;
+    var value = null;
+    if (this[key]) {
+      value = this[key];
+    } else if (typeof(Storage) !== "undefined") {
+      value = localStorage.getItem(keyStore);
+    }
+    return value;
+  };
+
+  /*
+   * A public facing helper method for signing up users
+   *
+   * @method signup
+   * @public
+   * @params {string} username
+   * @params {string} password
+   * @params {string} email
+   * @params {string} name
+   * @param {function} callback
+   * @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.signup = function(username, password, email, name, callback) {
+    var self = this;
+    var options = {
+      type: "users",
+      username: username,
+      password: password,
+      email: email,
+      name: name
+    };
+
+    this.createEntity(options, callback);
+  };
+
+  /*
+   *
+   *  A public method to log in an app user - stores the token for later use
+   *
+   *  @method login
+   *  @public
+   *  @params {string} username
+   *  @params {string} password
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.login = function(username, password, callback) {
+    var self = this;
+    var options = {
+      method: 'POST',
+      endpoint: 'token',
+      body: {
+        username: username,
+        password: password,
+        grant_type: 'password'
+      }
+    };
+    self.request(options, function(err, response) {
+      var user = {};
+      if (err) {
+        if (self.logging) console.log('error trying to log user in');
+      } else {
+        var options = {
+          client: self,
+          data: response.user
+        };
+        user = new Usergrid.Entity(options);
+        self.setToken(response.access_token);
+      }
+      doCallback(callback, [err, response, user]);
+    });
+  };
+  
+  Usergrid.Client.prototype.adminlogin = function(username, password, callback) {
+    var self = this;
+    var options = {
+        method: "POST",
+        endpoint:'management/token',
+        body: {
+            username: username,
+            password: password,
+            grant_type: "password"
+        },
+        mQuery:true
+    };
+    self.request(options, function(err, response) {
+        var user = {};
+        if (err) {
+            if (self.logging) console.log("error trying to log adminuser in");
+        } else {
+            var options = {
+                client: self,
+                data: response.user
+            };
+            user = new Usergrid.Entity(options);
+            self.setToken(response.access_token);
+        }
+        doCallback(callback, [ err, response, user ]);
+    });
+  };
+
+  Usergrid.Client.prototype.reAuthenticateLite = function(callback) {
+    var self = this;
+    var options = {
+      method: 'GET',
+      endpoint: 'management/me',
+      mQuery: true
+    };
+    this.request(options, function(err, response) {
+      if (err && self.logging) {
+        console.log('error trying to re-authenticate user');
+      } else {
+
+        //save the re-authed token and current email/username
+        self.setToken(response.data.access_token);
+
+      }
+      doCallback(callback, [err]);
+
+    });
+  };
+
+
+  Usergrid.Client.prototype.reAuthenticate = function(email, callback) {
+    var self = this;
+    var options = {
+      method: 'GET',
+      endpoint: 'management/users/' + email,
+      mQuery: true
+    };
+    this.request(options, function(err, response) {
+      var organizations = {};
+      var applications = {};
+      var user = {};
+      var data;
+      if (err && self.logging) {
+        console.log('error trying to full authenticate user');
+      } else {
+        data = response.data;
+        self.setToken(data.token);
+        self.set('email', data.email);
+
+        //delete next block and corresponding function when iframes are refactored
+        localStorage.setItem('accessToken', data.token);
+        localStorage.setItem('userUUID', data.uuid);
+        localStorage.setItem('userEmail', data.email);
+        //end delete block
+
+
+        var userData = {
+          "username": data.username,
+          "email": data.email,
+          "name": data.name,
+          "uuid": data.uuid
+        };
+        var options = {
+          client: self,
+          data: userData
+        };
+        user = new Usergrid.Entity(options);
+
+        organizations = data.organizations;
+        var org = '';
+        try {
+          //if we have an org stored, then use that one. Otherwise, use the first one.
+          var existingOrg = self.get('orgName');
+          org = (organizations[existingOrg]) ? organizations[existingOrg] : organizations[Object.keys(organizations)[0]];
+          self.set('orgName', org.name);
+        } catch (e) {
+          err = true;
+          if (self.logging) {
+            console.log('error selecting org');
+          }
+        } //should always be an org
+
+        applications = self.parseApplicationsArray(org);
+        self.selectFirstApp(applications);
+
+        self.setObject('organizations', organizations);
+        self.setObject('applications', applications);
+
+      }
+      doCallback(callback, [err, data, user, organizations, applications], self);
+    });
+  };
+
+  /*
+   *  A public method to log in an app user with facebook - stores the token for later use
+   *
+   *  @method loginFacebook
+   *  @public
+   *  @params {string} username
+   *  @params {string} password
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.loginFacebook = function(facebookToken, callback) {
+    var self = this;
+    var options = {
+      method: 'GET',
+      endpoint: 'auth/facebook',
+      qs: {
+        fb_access_token: facebookToken
+      }
+    };
+    this.request(options, function(err, data) {
+      var user = {};
+      if (err && self.logging) {
+        console.log('error trying to log user in');
+      } else {
+        var options = {
+          client: self,
+          data: data.user
+        };
+        user = new Usergrid.Entity(options);
+        self.setToken(data.access_token);
+      }
+      doCallback(callback, [err, data, user], self);
+    });
+  };
+
+  /*
+   *  A public method to get the currently logged in user entity
+   *
+   *  @method getLoggedInUser
+   *  @public
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+  Usergrid.Client.prototype.getLoggedInUser = function(callback) {
+    var self = this;
+    if (!this.getToken()) {
+        doCallback(callback, [new UsergridError("Access Token not set"), null, self], self);
+    } else {
+      var options = {
+        method: 'GET',
+        endpoint: 'users/me'
+      };
+      this.request(options, function(err, response) {
+        if (err) {
+          if (self.logging) {
+            console.log('error trying to log user in');
+          }
+          console.error(err, response);
+          doCallback(callback, [err, response, self], self);
+        } else {
+          var options = {
+            client: self,
+            data: response.getEntity()
+          };
+          var user = new Usergrid.Entity(options);
+          doCallback(callback, [null, response, user], self);
+        }
+      });
+    }
+  };
+
+  /*
+   *  A public method to test if a user is logged in - does not guarantee that the token is still valid,
+   *  but rather that one exists
+   *
+   *  @method isLoggedIn
+   *  @public
+   *  @return {boolean} Returns true the user is logged in (has token and uuid), false if not
+   */
+  Usergrid.Client.prototype.isLoggedIn = function() {
+    var token = this.getToken();
+    return "undefined" !== typeof token && token !== null;
+  };
+
+  /*
+   *  A public method to log out an app user - clears all user fields from client
+   *
+   *  @method logout
+   *  @public
+   *  @return none
+   */
+  Usergrid.Client.prototype.logout = function() {
+    this.setToken();
+  };
+
+  /*
+   *  A public method to destroy access tokens on the server
+   *
+   *  @method logout
+   *  @public
+   *  @param {string} username	the user associated with the token to revoke
+   *  @param {string} token set to 'null' to revoke the token of the currently logged in user
+   *    or set to token value to revoke a specific token
+   *  @param {string} revokeAll set to 'true' to revoke all tokens for the user
+   *  @return none
+   */
+  Usergrid.Client.prototype.destroyToken = function(username, token, revokeAll, callback) {
+    var options = {
+      client: self,
+      method: 'PUT',
+    };
+
+    if (revokeAll === true) {
+      options.endpoint = 'users/' + username + '/revoketokens';
+    } else if (token === null) {
+      options.endpoint = 'users/' + username + '/revoketoken?token=' + this.getToken();
+    } else {
+      options.endpoint = 'users/' + username + '/revoketoken?token=' + token;
+    }
+    this.request(options, function(err, data) {
+      if (err) {
+        if (self.logging) {
+          console.log('error destroying access token');
+        }
+        doCallback(callback, [err, data, null], self);
+      } else {
+        if (revokeAll === true) {
+          console.log('all user tokens invalidated');
+        } else {
+          console.log('token invalidated');
+        }
+        doCallback(callback, [err, data, null], self);
+      }
+    });
+  };
+
+  /*
+   *  A public method to log out an app user - clears all user fields from client
+   *  and destroys the access token on the server.
+   *
+   *  @method logout
+   *  @public
+   *  @param {string} username the user associated with the token to revoke
+   *  @param {string} token set to 'null' to revoke the token of the currently logged in user
+   *   or set to token value to revoke a specific token
+   *  @param {string} revokeAll set to 'true' to revoke all tokens for the user
+   *  @return none
+   */
+  Usergrid.Client.prototype.logoutAndDestroyToken = function(username, token, revokeAll, callback) {
+    if (username === null) {
+      console.log('username required to revoke tokens');
+    } else {
+      this.destroyToken(username, token, revokeAll, callback);
+      if (revokeAll === true || token === this.getToken() || token === null) {
+        this.setToken(null);
+      }
+    }
+  };
+
+  /*
+   *  A private method to build the curl call to display on the command line
+   *
+   *  @method buildCurlCall
+   *  @private
+   *  @param {object} options
+   *  @return {string} curl
+   */
+  Usergrid.Client.prototype.buildCurlCall = function(options) {
+    var curl = ['curl'];
+    var method = (options.method || 'GET').toUpperCase();
+    var body = options.body;
+    var uri = options.uri;
+
+    //curl - add the method to the command (no need to add anything for GET)
+    curl.push('-X');
+    curl.push((['POST', 'PUT', 'DELETE'].indexOf(method) >= 0) ? method : 'GET');
+
+    //curl - append the path
+    curl.push(uri);
+
+    if ("object" === typeof body && Object.keys(body).length > 0 && ['POST', 'PUT'].indexOf(method) !== -1) {
+      curl.push('-d');
+      curl.push("'" + JSON.stringify(body) + "'");
+    }
+    curl = curl.join(' ');
+    //log the curl command to the console
+    console.log(curl);
+
+    return curl;
+  };
+
+  Usergrid.Client.prototype.getDisplayImage = function(email, picture, size) {
+    size = size || 50;
+    var image = 'https://apigee.com/usergrid/images/user_profile.png';
+    try {
+      if (picture) {
+        image = picture;
+      } else if (email.length) {
+        image = 'https://secure.gravatar.com/avatar/' + MD5(email) + '?s=' + size + encodeURI("&d=https://apigee.com/usergrid/images/user_profile.png");
+      }
+    } catch (e) {
+      //TODO do something with this error?
+    } finally {
+      return image;
+    }
+  };
+  global[name] = Usergrid.Client;
+  global[name].noConflict = function() {
+    if (overwrittenName) {
+      global[name] = overwrittenName;
+    }
+    return exports;
+  };
+  return global[name];
+}());
diff --git a/lib/modules/Collection.js b/lib/modules/Collection.js
new file mode 100644
index 0000000..d137125
--- /dev/null
+++ b/lib/modules/Collection.js
@@ -0,0 +1,483 @@
+/*
+ *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.
+ */
+
+
+/*
+ *  The Collection class models Usergrid Collections.  It essentially
+ *  acts as a container for holding Entity objects, while providing
+ *  additional funcitonality such as paging, and saving
+ *
+ *  @constructor
+ *  @param {string} options - configuration object
+ *  @return {Collection} collection
+ */
+Usergrid.Collection = function(options) {
+
+  if (options) {
+    this._client = options.client;
+    this._type = options.type;
+    this.qs = options.qs || {};
+
+    //iteration
+    this._list = options.list || [];
+    this._iterator = options.iterator || -1; //first thing we do is increment, so set to -1
+
+    //paging
+    this._previous = options.previous || [];
+    this._next = options.next || null;
+    this._cursor = options.cursor || null;
+
+    //restore entities if available
+    if (options.list) {
+      var count = options.list.length;
+      for (var i = 0; i < count; i++) {
+        //make new entity with
+        var entity = this._client.restoreEntity(options.list[i]);
+        this._list[i] = entity;
+      }
+    }
+  }
+  /*if (callback) {
+    //populate the collection
+    this.fetch(callback);
+  }*/
+
+};
+
+
+/*
+ *  method to determine whether or not the passed variable is a Usergrid Collection
+ *
+ *  @method isCollection
+ *  @public
+ *  @params {any} obj - any variable
+ *  @return {boolean} Returns true or false
+ */
+Usergrid.isCollection = function(obj) {
+  return (obj && obj instanceof Usergrid.Collection);
+};
+
+
+/*
+ *  gets the data from the collection object for serialization
+ *
+ *  @method serialize
+ *  @return {object} data
+ */
+Usergrid.Collection.prototype.serialize = function() {
+
+  //pull out the state from this object and return it
+  var data = {};
+  data.type = this._type;
+  data.qs = this.qs;
+  data.iterator = this._iterator;
+  data.previous = this._previous;
+  data.next = this._next;
+  data.cursor = this._cursor;
+
+  this.resetEntityPointer();
+  var i = 0;
+  data.list = [];
+  while (this.hasNextEntity()) {
+    var entity = this.getNextEntity();
+    data.list[i] = entity.serialize();
+    i++;
+  }
+
+  data = JSON.stringify(data);
+  return data;
+};
+//addCollection is deprecated?
+/*Usergrid.Collection.prototype.addCollection = function (collectionName, options, callback) {
+  self = this;
+  options.client = this._client;
+  var collection = new Usergrid.Collection(options, function(err, data) {
+    if (typeof(callback) === 'function') {
+
+      collection.resetEntityPointer();
+      while(collection.hasNextEntity()) {
+        var user = collection.getNextEntity();
+        var email = user.get('email');
+        var image = self._client.getDisplayImage(user.get('email'), user.get('picture'));
+        user._portal_image_icon = image;
+      }
+
+      self[collectionName] = collection;
+      doCallback(callback, [err, collection], self);
+    }
+  });
+};*/
+
+/*
+ *  Populates the collection from the server
+ *
+ *  @method fetch
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.fetch = function(callback) {
+  var self = this;
+  var qs = this.qs;
+
+  //add in the cursor if one is available
+  if (this._cursor) {
+    qs.cursor = this._cursor;
+  } else {
+    delete qs.cursor;
+  }
+  var options = {
+    method: 'GET',
+    endpoint: this._type,
+    qs: this.qs
+  };
+  this._client.request(options, function(err, response) {
+    if (err && self._client.logging) {
+      console.log('error getting collection');
+    } else {
+      //save the cursor if there is one
+      self.saveCursor(response.cursor || null);
+      self.resetEntityPointer();
+      //save entities locally
+      self._list = response.getEntities()
+        .filter(function(entity) {
+          return isUUID(entity.uuid);
+        })
+        .map(function(entity) {
+          var ent = new Usergrid.Entity({
+            client: self._client
+          });
+          ent.set(entity);
+          ent.type = self._type;
+          //ent._json = JSON.stringify(entity, null, 2);
+          return ent;
+        });
+    }
+    doCallback(callback, [err, response, self], self);
+  });
+};
+
+/*
+ *  Adds a new Entity to the collection (saves, then adds to the local object)
+ *
+ *  @method addNewEntity
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data, entity)
+ */
+Usergrid.Collection.prototype.addEntity = function(entityObject, callback) {
+  var self = this;
+  entityObject.type = this._type;
+
+  //create the new entity
+  this._client.createEntity(entityObject, function(err, response, entity) {
+    if (!err) {
+      //then add the entity to the list
+      self.addExistingEntity(entity);
+    }
+    doCallback(callback, [err, response, self], self);
+  });
+};
+
+Usergrid.Collection.prototype.addExistingEntity = function(entity) {
+  //entity should already exist in the db, so just add it to the list
+  var count = this._list.length;
+  this._list[count] = entity;
+};
+
+/*
+ *  Removes the Entity from the collection, then destroys the object on the server
+ *
+ *  @method destroyEntity
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.destroyEntity = function(entity, callback) {
+  var self = this;
+  entity.destroy(function(err, response) {
+    if (err) {
+      if (self._client.logging) {
+        console.log('could not destroy entity');
+      }
+      doCallback(callback, [err, response, self], self);
+    } else {
+      //destroy was good, so repopulate the collection
+      self.fetch(callback);
+    }
+    //remove entity from the local store
+    self.removeEntity(entity);
+  });
+};
+
+/*
+ * Filters the list of entities based on the supplied criteria function
+ * works like Array.prototype.filter
+ *
+ *  @method getEntitiesByCriteria
+ *  @param {function} criteria  A function that takes each entity as an argument and returns true or false
+ *  @return {Entity[]} returns a list of entities that caused the criteria function to return true
+ */
+Usergrid.Collection.prototype.getEntitiesByCriteria = function(criteria) {
+  return this._list.filter(criteria);
+};
+/*
+ * Returns the first entity from the list of entities based on the supplied criteria function
+ * works like Array.prototype.filter
+ *
+ *  @method getEntitiesByCriteria
+ *  @param {function} criteria  A function that takes each entity as an argument and returns true or false
+ *  @return {Entity[]} returns a list of entities that caused the criteria function to return true
+ */
+Usergrid.Collection.prototype.getEntityByCriteria = function(criteria) {
+  return this.getEntitiesByCriteria(criteria).shift();
+};
+/*
+ * Removed an entity from the collection without destroying it on the server
+ *
+ *  @method removeEntity
+ *  @param {object} entity
+ *  @return {Entity} returns the removed entity or undefined if it was not found
+ */
+Usergrid.Collection.prototype.removeEntity = function(entity) {
+  var removedEntity = this.getEntityByCriteria(function(item) {
+    return entity.uuid === item.get('uuid');
+  });
+  delete this._list[this._list.indexOf(removedEntity)];
+  return removedEntity;
+};
+
+/*
+ *  Looks up an Entity by UUID
+ *
+ *  @method getEntityByUUID
+ *  @param {string} UUID
+ *  @param {function} callback
+ *  @return {callback} callback(err, data, entity)
+ */
+Usergrid.Collection.prototype.getEntityByUUID = function(uuid, callback) {
+  var entity = this.getEntityByCriteria(function(item) {
+    return item.get('uuid') === uuid;
+  });
+  if (entity) {
+    doCallback(callback, [null, entity, entity], this);
+  } else {
+    //get the entity from the database
+    var options = {
+      data: {
+        type: this._type,
+        uuid: uuid
+      },
+      client: this._client
+    };
+    entity = new Usergrid.Entity(options);
+    entity.fetch(callback);
+  }
+};
+
+/*
+ *  Returns the first Entity of the Entity list - does not affect the iterator
+ *
+ *  @method getFirstEntity
+ *  @return {object} returns an entity object
+ */
+Usergrid.Collection.prototype.getFirstEntity = function() {
+  var count = this._list.length;
+  if (count > 0) {
+    return this._list[0];
+  }
+  return null;
+};
+
+/*
+ *  Returns the last Entity of the Entity list - does not affect the iterator
+ *
+ *  @method getLastEntity
+ *  @return {object} returns an entity object
+ */
+Usergrid.Collection.prototype.getLastEntity = function() {
+  var count = this._list.length;
+  if (count > 0) {
+    return this._list[count - 1];
+  }
+  return null;
+};
+
+/*
+ *  Entity iteration -Checks to see if there is a "next" entity
+ *  in the list.  The first time this method is called on an entity
+ *  list, or after the resetEntityPointer method is called, it will
+ *  return true referencing the first entity in the list
+ *
+ *  @method hasNextEntity
+ *  @return {boolean} true if there is a next entity, false if not
+ */
+Usergrid.Collection.prototype.hasNextEntity = function() {
+  var next = this._iterator + 1;
+  var hasNextElement = (next >= 0 && next < this._list.length);
+  if (hasNextElement) {
+    return true;
+  }
+  return false;
+};
+
+/*
+ *  Entity iteration - Gets the "next" entity in the list.  The first
+ *  time this method is called on an entity list, or after the method
+ *  resetEntityPointer is called, it will return the,
+ *  first entity in the list
+ *
+ *  @method hasNextEntity
+ *  @return {object} entity
+ */
+Usergrid.Collection.prototype.getNextEntity = function() {
+  this._iterator++;
+  var hasNextElement = (this._iterator >= 0 && this._iterator <= this._list.length);
+  if (hasNextElement) {
+    return this._list[this._iterator];
+  }
+  return false;
+};
+
+/*
+ *  Entity iteration - Checks to see if there is a "previous"
+ *  entity in the list.
+ *
+ *  @method hasPrevEntity
+ *  @return {boolean} true if there is a previous entity, false if not
+ */
+Usergrid.Collection.prototype.hasPrevEntity = function() {
+  var previous = this._iterator - 1;
+  var hasPreviousElement = (previous >= 0 && previous < this._list.length);
+  if (hasPreviousElement) {
+    return true;
+  }
+  return false;
+};
+
+/*
+ *  Entity iteration - Gets the "previous" entity in the list.
+ *
+ *  @method getPrevEntity
+ *  @return {object} entity
+ */
+Usergrid.Collection.prototype.getPrevEntity = function() {
+  this._iterator--;
+  var hasPreviousElement = (this._iterator >= 0 && this._iterator <= this._list.length);
+  if (hasPreviousElement) {
+    return this._list[this._iterator];
+  }
+  return false;
+};
+
+/*
+ *  Entity iteration - Resets the iterator back to the beginning
+ *  of the list
+ *
+ *  @method resetEntityPointer
+ *  @return none
+ */
+Usergrid.Collection.prototype.resetEntityPointer = function() {
+  this._iterator = -1;
+};
+
+/*
+ * Method to save off the cursor just returned by the last API call
+ *
+ * @public
+ * @method saveCursor
+ * @return none
+ */
+Usergrid.Collection.prototype.saveCursor = function(cursor) {
+  //if current cursor is different, grab it for next cursor
+  if (this._next !== cursor) {
+    this._next = cursor;
+  }
+};
+
+/*
+ * Resets the paging pointer (back to original page)
+ *
+ * @public
+ * @method resetPaging
+ * @return none
+ */
+Usergrid.Collection.prototype.resetPaging = function() {
+  this._previous = [];
+  this._next = null;
+  this._cursor = null;
+};
+
+/*
+ *  Paging -  checks to see if there is a next page od data
+ *
+ *  @method hasNextPage
+ *  @return {boolean} returns true if there is a next page of data, false otherwise
+ */
+Usergrid.Collection.prototype.hasNextPage = function() {
+  return (this._next);
+};
+
+/*
+ *  Paging - advances the cursor and gets the next
+ *  page of data from the API.  Stores returned entities
+ *  in the Entity list.
+ *
+ *  @method getNextPage
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.getNextPage = function(callback) {
+  if (this.hasNextPage()) {
+    //set the cursor to the next page of data
+    this._previous.push(this._cursor);
+    this._cursor = this._next;
+    //empty the list
+    this._list = [];
+    this.fetch(callback);
+  }
+};
+
+/*
+ *  Paging -  checks to see if there is a previous page od data
+ *
+ *  @method hasPreviousPage
+ *  @return {boolean} returns true if there is a previous page of data, false otherwise
+ */
+Usergrid.Collection.prototype.hasPreviousPage = function() {
+  return (this._previous.length > 0);
+};
+
+/*
+ *  Paging - reverts the cursor and gets the previous
+ *  page of data from the API.  Stores returned entities
+ *  in the Entity list.
+ *
+ *  @method getPreviousPage
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.getPreviousPage = function(callback) {
+  if (this.hasPreviousPage()) {
+    this._next = null; //clear out next so the comparison will find the next item
+    this._cursor = this._previous.pop();
+    //empty the list
+    this._list = [];
+    this.fetch(callback);
+  }
+};
diff --git a/lib/modules/Counter.js b/lib/modules/Counter.js
new file mode 100644
index 0000000..7b120cc
--- /dev/null
+++ b/lib/modules/Counter.js
@@ -0,0 +1,196 @@
+/*
+ *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.
+ */
+
+
+/*
+ *  A class to model a Usergrid event.
+ *
+ *  @constructor
+ *  @param {object} options {timestamp:0, category:'value', counters:{name : value}}
+ *  @returns {callback} callback(err, event)
+ */
+Usergrid.Counter = function(options) {
+  // var self=this;
+  this._client = options.client;
+  this._data = options.data || {};
+  this._data.category = options.category || "UNKNOWN";
+  this._data.timestamp = options.timestamp || 0;
+  this._data.type = "events";
+  this._data.counters = options.counters || {};
+  // doCallback(callback, [false, this], this);
+  //this.save(callback);
+};
+var COUNTER_RESOLUTIONS = [
+  'all', 'minute', 'five_minutes', 'half_hour',
+  'hour', 'six_day', 'day', 'week', 'month'
+];
+/*
+ *  Inherit from Usergrid.Entity.
+ *  Note: This only accounts for data on the group object itself.
+ *  You need to use add and remove to manipulate group membership.
+ */
+Usergrid.Counter.prototype = new Usergrid.Entity();
+
+/*
+ * overrides Entity.prototype.fetch. Returns all data for counters
+ * associated with the object as specified in the constructor
+ *
+ * @public
+ * @method increment
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Counter.prototype.fetch = function(callback) {
+  this.getData({}, callback);
+};
+/*
+ * increments the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method increment
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Counter.prototype.increment = function(options, callback) {
+  var self = this,
+    name = options.name,
+    value = options.value;
+  if (!name) {
+    return doCallback(callback, [new UsergridInvalidArgumentError("'name' for increment, decrement must be a number"), null, self], self);
+  } else if (isNaN(value)) {
+    return doCallback(callback, [new UsergridInvalidArgumentError("'value' for increment, decrement must be a number"), null, self], self);
+  } else {
+    self._data.counters[name] = (parseInt(value)) || 1;
+    return self.save(callback);
+  }
+};
+/*
+ * decrements the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method decrement
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+
+Usergrid.Counter.prototype.decrement = function(options, callback) {
+  var self = this,
+    name = options.name,
+    value = options.value;
+  self.increment({
+    name: name,
+    value: -((parseInt(value)) || 1)
+  }, callback);
+};
+/*
+ * resets the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method reset
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+
+Usergrid.Counter.prototype.reset = function(options, callback) {
+  var self = this,
+    name = options.name;
+  self.increment({
+    name: name,
+    value: 0
+  }, callback);
+};
+
+/*
+ * gets data for one or more counters over a given
+ * time period at a specified resolution
+ *
+ * options object: {
+ *                   counters: ['counter1', 'counter2', ...],
+ *                   start: epoch timestamp or ISO date string,
+ *                   end: epoch timestamp or ISO date string,
+ *                   resolution: one of ('all', 'minute', 'five_minutes', 'half_hour', 'hour', 'six_day', 'day', 'week', or 'month')
+ *                   }
+ *
+ * @public
+ * @method getData
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Counter.prototype.getData = function(options, callback) {
+  var start_time,
+    end_time,
+    start = options.start || 0,
+    end = options.end || Date.now(),
+    resolution = (options.resolution || 'all').toLowerCase(),
+    counters = options.counters || Object.keys(this._data.counters),
+    res = (resolution || 'all').toLowerCase();
+  if (COUNTER_RESOLUTIONS.indexOf(res) === -1) {
+    res = 'all';
+  }
+  start_time = getSafeTime(start);
+  end_time = getSafeTime(end);
+  var self = this;
+  //https://api.usergrid.com/yourorgname/sandbox/counters?counter=test_counter
+  var params = Object.keys(counters).map(function(counter) {
+    return ["counter", encodeURIComponent(counters[counter])].join('=');
+  });
+  params.push('resolution=' + res);
+  params.push('start_time=' + String(start_time));
+  params.push('end_time=' + String(end_time));
+
+  var endpoint = "counters?" + params.join('&');
+  this._client.request({
+    endpoint: endpoint
+  }, function(err, data) {
+    if (data.counters && data.counters.length) {
+      data.counters.forEach(function(counter) {
+        self._data.counters[counter.name] = counter.value || counter.values;
+      });
+    }
+    return doCallback(callback, [err, data, self], self);
+  });
+};
+
+function getSafeTime(prop) {
+  var time;
+  switch (typeof prop) {
+    case "undefined":
+      time = Date.now();
+      break;
+    case "number":
+      time = prop;
+      break;
+    case "string":
+      time = (isNaN(prop)) ? Date.parse(prop) : parseInt(prop);
+      break;
+    default:
+      time = Date.parse(prop.toString());
+  }
+  return time;
+}
diff --git a/lib/modules/Entity.js b/lib/modules/Entity.js
new file mode 100644
index 0000000..029951c
--- /dev/null
+++ b/lib/modules/Entity.js
@@ -0,0 +1,767 @@
+/*
+ *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.
+ */
+
+
+var ENTITY_SYSTEM_PROPERTIES = ['metadata', 'created', 'modified', 'oldpassword', 'newpassword', 'type', 'activated', 'uuid'];
+
+/*
+ *  A class to Model a Usergrid Entity.
+ *  Set the type and uuid of entity in the 'data' json object
+ *
+ *  @constructor
+ *  @param {object} options {client:client, data:{'type':'collection_type', uuid:'uuid', 'key':'value'}}
+ */
+Usergrid.Entity = function(options) {
+  this._data={};
+  this._client=undefined;
+  if (options) {
+    //this._data = options.data || {};
+    this.set(options.data || {});
+    this._client = options.client || {};
+  }
+};
+
+/*
+ *  method to determine whether or not the passed variable is a Usergrid Entity
+ *
+ *  @method isEntity
+ *  @public
+ *  @params {any} obj - any variable
+ *  @return {boolean} Returns true or false
+ */
+Usergrid.Entity.isEntity = function(obj) {
+  return (obj && obj instanceof Usergrid.Entity);
+};
+
+/*
+ *  method to determine whether or not the passed variable is a Usergrid Entity
+ *  That has been saved.
+ *
+ *  @method isPersistedEntity
+ *  @public
+ *  @params {any} obj - any variable
+ *  @return {boolean} Returns true or false
+ */
+Usergrid.Entity.isPersistedEntity = function(obj) {
+  return (isEntity(obj) && isUUID(obj.get('uuid')));
+};
+
+/*
+ *  returns a serialized version of the entity object
+ *
+ *  Note: use the client.restoreEntity() function to restore
+ *
+ *  @method serialize
+ *  @return {string} data
+ */
+Usergrid.Entity.prototype.serialize = function() {
+  return JSON.stringify(this._data);
+};
+
+/*
+ *  gets a specific field or the entire data object. If null or no argument
+ *  passed, will return all data, else, will return a specific field
+ *
+ *  @method get
+ *  @param {string} field
+ *  @return {string} || {object} data
+ */
+Usergrid.Entity.prototype.get = function(key) {
+  var value;
+  if (arguments.length === 0) {
+    value = this._data;
+  } else if (arguments.length > 1) {
+    key = [].slice.call(arguments).reduce(function(p, c, i, a) {
+      if (c instanceof Array) {
+        p = p.concat(c);
+      } else {
+        p.push(c);
+      }
+      return p;
+    }, []);
+  }
+  if (key instanceof Array) {
+    var self = this;
+    value = key.map(function(k) {
+      return self.get(k);
+    });
+  } else if ("undefined" !== typeof key) {
+    value = this._data[key];
+  }
+  return value;
+};
+/*
+ *  adds a specific key value pair or object to the Entity's data
+ *  is additive - will not overwrite existing values unless they
+ *  are explicitly specified
+ *
+ *  @method set
+ *  @param {string} key || {object}
+ *  @param {string} value
+ *  @return none
+ */
+Usergrid.Entity.prototype.set = function(key, value) {
+  if (typeof key === 'object') {
+    for (var field in key) {
+      this._data[field] = key[field];
+    }
+  } else if (typeof key === 'string') {
+    if (value === null) {
+      delete this._data[key];
+    } else {
+      this._data[key] = value;
+    }
+  } else {
+    this._data = {};
+  }
+};
+
+Usergrid.Entity.prototype.getEndpoint = function() {
+  var type = this.get('type'),
+    nameProperties = ['uuid', 'name'],
+    name;
+  if (type === undefined) {
+    throw new UsergridError('cannot fetch entity, no entity type specified', 'no_type_specified');
+  } else if (/^users?$/.test(type)) {
+    nameProperties.unshift('username');
+  }
+  name = this.get(nameProperties)
+    .filter(function(x) {
+      return (x !== null && "undefined" !== typeof x);
+    })
+    .shift();
+  return (name) ? [type, name].join('/') : type;
+};
+/*
+ *  Saves the entity back to the database
+ *
+ *  @method save
+ *  @public
+ *  @param {function} callback
+ *  @return {callback} callback(err, response, self)
+ */
+Usergrid.Entity.prototype.save = function(callback) {
+  var self = this,
+    type = this.get('type'),
+    method = 'POST',
+    entityId = this.get("uuid"),
+    changePassword,
+    entityData = this.get(),
+    options = {
+      method: method,
+      endpoint: type
+    };
+  //update the entity if the UUID is present
+  if (entityId) {
+    options.method = 'PUT';
+    options.endpoint += '/' + entityId;
+  }
+  //remove system-specific properties
+  options.body = Object.keys(entityData)
+    .filter(function(key) {
+      return (ENTITY_SYSTEM_PROPERTIES.indexOf(key) === -1);
+    })
+    .reduce(function(data, key) {
+      data[key] = entityData[key];
+      return data;
+    }, {});
+  self._client.request(options, function(err, response) {
+    var entity = response.getEntity();
+    if (entity) {
+      self.set(entity);
+      self.set('type', (/^\//.test(response.path)) ? response.path.substring(1) : response.path);
+    }
+    if (err && self._client.logging) {
+      console.log('could not save entity');
+    }
+    doCallback(callback, [err, response, self], self);
+  });
+};
+
+/*
+ *
+ * Updates the user's password
+ */
+Usergrid.Entity.prototype.changePassword = function(oldpassword, newpassword, callback) {
+  //Note: we have a ticket in to change PUT calls to /users to accept the password change
+  //      once that is done, we will remove this call and merge it all into one
+  var self = this;
+  if ("function" === typeof oldpassword && callback === undefined) {
+    callback = oldpassword;
+    oldpassword = self.get("oldpassword");
+    newpassword = self.get("newpassword");
+  }
+  //clear out pw info if present
+  self.set({
+    'password': null,
+    'oldpassword': null,
+    'newpassword': null
+  });
+  if ((/^users?$/.test(self.get('type'))) && oldpassword && newpassword) {
+    var options = {
+      method: 'PUT',
+      endpoint: 'users/' + self.get("uuid") + '/password',
+      body: {
+        uuid: self.get("uuid"),
+        username: self.get("username"),
+        oldpassword: oldpassword,
+        newpassword: newpassword
+      }
+    };
+    self._client.request(options, function(err, response) {
+      if (err && self._client.logging) {
+        console.log('could not update user');
+      }
+      //remove old and new password fields so they don't end up as part of the entity object
+      doCallback(callback, [err, response, self], self);
+    });
+  } else {
+    throw new UsergridInvalidArgumentError("Invalid arguments passed to 'changePassword'");
+  }
+};
+/*
+ *  refreshes the entity by making a GET call back to the database
+ *
+ *  @method fetch
+ *  @public
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Entity.prototype.fetch = function(callback) {
+  var endpoint, self = this;
+  endpoint = this.getEndpoint();
+  var options = {
+    method: 'GET',
+    endpoint: endpoint
+  };
+  this._client.request(options, function(err, response) {
+    var entity = response.getEntity();
+    if (entity) {
+      self.set(entity);
+    }
+    doCallback(callback, [err, response, self], self);
+  });
+};
+
+/*
+ *  deletes the entity from the database - will only delete
+ *  if the object has a valid uuid
+ *
+ *  @method destroy
+ *  @public
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.destroy = function(callback) {
+  var self = this;
+  var endpoint = this.getEndpoint();
+
+  var options = {
+    method: 'DELETE',
+    endpoint: endpoint
+  };
+  this._client.request(options, function(err, response) {
+    if (!err) {
+      self.set(null);
+    }
+    doCallback(callback, [err, response, self], self);
+  });
+};
+
+/*
+ *  connects one entity to another
+ *
+ *  @method connect
+ *  @public
+ *  @param {string} connection
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.connect = function(connection, entity, callback) {
+  this.addOrRemoveConnection("POST", connection, entity, callback);
+};
+
+/*
+ *  disconnects one entity from another
+ *
+ *  @method disconnect
+ *  @public
+ *  @param {string} connection
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.disconnect = function(connection, entity, callback) {
+  this.addOrRemoveConnection("DELETE", connection, entity, callback);
+};
+/*
+ *  adds or removes a connection between two entities
+ *
+ *  @method addOrRemoveConnection
+ *  @public
+ *  @param {string} method
+ *  @param {string} connection
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.addOrRemoveConnection = function(method, connection, entity, callback) {
+  var self = this;
+  if (['POST', 'DELETE'].indexOf(method.toUpperCase()) == -1) {
+    throw new UsergridInvalidArgumentError("invalid method for connection call. must be 'POST' or 'DELETE'");
+  }
+  //connectee info
+  var connecteeType = entity.get('type');
+  var connectee = this.getEntityId(entity);
+  if (!connectee) {
+    throw new UsergridInvalidArgumentError("connectee could not be identified");
+  }
+
+  //connector info
+  var connectorType = this.get('type');
+  var connector = this.getEntityId(this);
+  if (!connector) {
+    throw new UsergridInvalidArgumentError("connector could not be identified");
+  }
+
+  var endpoint = [connectorType, connector, connection, connecteeType, connectee].join('/');
+  var options = {
+    method: method,
+    endpoint: endpoint
+  };
+  this._client.request(options, function(err, response) {
+    if (err && self._client.logging) {
+      console.log('There was an error with the connection call');
+    }
+    doCallback(callback, [err, response, self], self);
+  });
+};
+
+/*
+ *  returns a unique identifier for an entity
+ *
+ *  @method connect
+ *  @public
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.getEntityId = function(entity) {
+    var id;
+    if (isUUID(entity.get("uuid"))) {
+        id = entity.get("uuid");
+    } else if (this.get("type") === "users" || this.get("type") === "user") {
+        id = entity.get("username");
+    } else  {
+        id = entity.get("name");
+    }
+    return id;
+};
+
+/*
+ *  gets an entities connections
+ *
+ *  @method getConnections
+ *  @public
+ *  @param {string} connection
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data, connections)
+ *
+ */
+Usergrid.Entity.prototype.getConnections = function(connection, callback) {
+
+  var self = this;
+
+  //connector info
+  var connectorType = this.get('type');
+  var connector = this.getEntityId(this);
+  if (!connector) {
+    if (typeof(callback) === 'function') {
+      var error = 'Error in getConnections - no uuid specified.';
+      if (self._client.logging) {
+        console.log(error);
+      }
+      doCallback(callback, [true, error], self);
+    }
+    return;
+  }
+
+  var endpoint = connectorType + '/' + connector + '/' + connection + '/';
+  var options = {
+    method: 'GET',
+    endpoint: endpoint
+  };
+  this._client.request(options, function(err, data) {
+    if (err && self._client.logging) {
+      console.log('entity could not be connected');
+    }
+
+    self[connection] = {};
+
+    var length = (data && data.entities) ? data.entities.length : 0;
+    for (var i = 0; i < length; i++) {
+      if (data.entities[i].type === 'user') {
+        self[connection][data.entities[i].username] = data.entities[i];
+      } else {
+        self[connection][data.entities[i].name] = data.entities[i];
+      }
+    }
+
+    doCallback(callback, [err, data, data.entities], self);
+  });
+
+};
+
+Usergrid.Entity.prototype.getGroups = function(callback) {
+
+  var self = this;
+
+  var endpoint = 'users' + '/' + this.get('uuid') + '/groups';
+  var options = {
+    method: 'GET',
+    endpoint: endpoint
+  };
+  this._client.request(options, function(err, data) {
+    if (err && self._client.logging) {
+      console.log('entity could not be connected');
+    }
+
+    self.groups = data.entities;
+
+    doCallback(callback, [err, data, data.entities], self);
+  });
+
+};
+
+Usergrid.Entity.prototype.getActivities = function(callback) {
+
+  var self = this;
+
+  var endpoint = this.get('type') + '/' + this.get('uuid') + '/activities';
+  var options = {
+    method: 'GET',
+    endpoint: endpoint
+  };
+  this._client.request(options, function(err, data) {
+    if (err && self._client.logging) {
+      console.log('entity could not be connected');
+    }
+
+    for (var entity in data.entities) {
+      data.entities[entity].createdDate = (new Date(data.entities[entity].created)).toUTCString();
+    }
+
+    self.activities = data.entities;
+
+    doCallback(callback, [err, data, data.entities], self);
+  });
+
+};
+
+Usergrid.Entity.prototype.getFollowing = function(callback) {
+
+  var self = this;
+
+  var endpoint = 'users' + '/' + this.get('uuid') + '/following';
+  var options = {
+    method: 'GET',
+    endpoint: endpoint
+  };
+  this._client.request(options, function(err, data) {
+    if (err && self._client.logging) {
+      console.log('could not get user following');
+    }
+
+    for (var entity in data.entities) {
+      data.entities[entity].createdDate = (new Date(data.entities[entity].created)).toUTCString();
+      var image = self._client.getDisplayImage(data.entities[entity].email, data.entities[entity].picture);
+      data.entities[entity]._portal_image_icon = image;
+    }
+
+    self.following = data.entities;
+
+    doCallback(callback, [err, data, data.entities], self);
+  });
+
+};
+
+
+Usergrid.Entity.prototype.getFollowers = function(callback) {
+
+  var self = this;
+
+  var endpoint = 'users' + '/' + this.get('uuid') + '/followers';
+  var options = {
+    method: 'GET',
+    endpoint: endpoint
+  };
+  this._client.request(options, function(err, data) {
+    if (err && self._client.logging) {
+      console.log('could not get user followers');
+    }
+
+    for (var entity in data.entities) {
+      data.entities[entity].createdDate = (new Date(data.entities[entity].created)).toUTCString();
+      var image = self._client.getDisplayImage(data.entities[entity].email, data.entities[entity].picture);
+      data.entities[entity]._portal_image_icon = image;
+    }
+
+    self.followers = data.entities;
+
+    doCallback(callback, [err, data, data.entities], self);
+  });
+
+};
+
+Usergrid.Client.prototype.createRole = function(roleName, permissions, callback) {
+    
+    var options = {
+        type: 'role',
+        name: roleName        
+    };
+
+    this.createEntity(options, function(err, response, entity) {
+        if (err) {
+            doCallback(callback, [ err, response, self ]);    
+        } else {
+            entity.assignPermissions(permissions, function (err, data) {
+                if (err) {
+                    doCallback(callback, [ err, response, self ]);    
+                } else {
+                    doCallback(callback, [ err, data, data.data ], self);
+                }
+            })
+        }        
+    });
+
+};
+
+Usergrid.Entity.prototype.getRoles = function(callback) {
+    var self = this;
+    var endpoint = this.get("type") + "/" + this.get("uuid") + "/roles";
+    var options = {
+        method: "GET",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log("could not get user roles");
+        }
+        self.roles = data.entities;
+        doCallback(callback, [ err, data, data.entities ], self);
+    });
+};
+
+Usergrid.Entity.prototype.assignRole = function(roleName, callback) {
+    
+    var self = this;
+    var type = self.get('type');
+    var collection = type + 's';
+    var entityID;
+
+    if (type == 'user' && this.get('username') != null) {
+        entityID = self.get('username');
+    } else if (type == 'group' && this.get('name') != null) {
+        entityID = self.get('name');
+    } else if (this.get('uuid') != null) {
+        entityID = self.get('uuid');
+    }
+
+    if (type != 'users' && type != 'groups') {
+        doCallback(callback, [ new UsergridError('entity must be a group or user', 'invalid_entity_type'), null, this ], this);
+    }
+
+    var endpoint = 'roles/' + roleName + '/' + collection + '/' + entityID;
+    var options = {
+        method: 'POST',
+        endpoint: endpoint        
+    };
+
+    this._client.request(options, function(err, response) {
+        if (err) {
+            console.log('Could not assign role.');
+        }        
+        doCallback(callback, [ err, response, self ]);
+    });
+
+};
+
+Usergrid.Entity.prototype.removeRole = function(roleName, callback) {
+    
+    var self = this;
+    var type = self.get('type');
+    var collection = type + 's';
+    var entityID;
+
+    if (type == 'user' && this.get('username') != null) {
+        entityID = this.get('username');
+    } else if (type == 'group' && this.get('name') != null) {
+        entityID = this.get('name');
+    } else if (this.get('uuid') != null) {
+        entityID = this.get('uuid');
+    }
+
+    if (type != 'users' && type != 'groups') {
+        doCallback(callback, [ new UsergridError('entity must be a group or user', 'invalid_entity_type'), null, this ], this);
+    }
+
+    var endpoint = 'roles/' + roleName + '/' + collection + '/' + entityID;
+    var options = {
+        method: 'DELETE',
+        endpoint: endpoint        
+    };
+
+    this._client.request(options, function(err, response) {
+        if (err) {
+            console.log('Could not assign role.');
+        }        
+        doCallback(callback, [ err, response, self ]);
+    });
+
+};
+
+Usergrid.Entity.prototype.assignPermissions = function(permissions, callback) {
+    var self = this;
+    var entityID;
+    var type = this.get('type');
+
+    if (type != 'user' && type != 'users' && type != 'group' && type != 'groups' && type != 'role' && type != 'roles') {
+        doCallback(callback, [ new UsergridError('entity must be a group, user, or role', 'invalid_entity_type'), null, this ], this);
+    }
+
+    if (type == 'user' && this.get('username') != null) {
+        entityID = this.get('username');
+    } else if (type == 'group' && this.get('name') != null) {
+        entityID = this.get('name');
+    } else if (this.get('uuid') != null) {
+        entityID = this.get('uuid');
+    }
+
+    var endpoint = type + '/' + entityID + '/permissions';
+    var options = {
+        method: 'POST',
+        endpoint: endpoint,
+        body: {
+            'permission': permissions
+        }
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log('could not assign permissions');
+        }
+        doCallback(callback, [ err, data, data.data ], self);
+    });
+};
+
+Usergrid.Entity.prototype.removePermissions = function(permissions, callback) {
+    var self = this;
+    var entityID;
+    var type = this.get('type');
+
+    if (type != 'user' && type != 'users' && type != 'group' && type != 'groups' && type != 'role' && type != 'roles') {
+        doCallback(callback, [ new UsergridError('entity must be a group, user, or role', 'invalid_entity_type'), null, this ], this);
+    }
+
+    if (type == 'user' && this.get('username') != null) {
+        entityID = this.get('username');
+    } else if (type == 'group' && this.get('name') != null) {
+        entityID = this.get('name');
+    } else if (this.get('uuid') != null) {
+        entityID = this.get('uuid');
+    }
+
+    var endpoint = type + '/' + entityID + '/permissions';
+    var options = {
+        method: 'DELETE',
+        endpoint: endpoint,
+        qs: {
+            'permission': permissions
+        }
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log('could not remove permissions');
+        }
+        doCallback(callback, [ err, data, data.params.permission ], self);
+    });
+};
+
+Usergrid.Entity.prototype.getPermissions = function(callback) {
+
+  var self = this;
+
+  var endpoint = this.get('type') + '/' + this.get('uuid') + '/permissions';
+  var options = {
+    method: 'GET',
+    endpoint: endpoint
+  };
+  this._client.request(options, function(err, data) {
+    if (err && self._client.logging) {
+      console.log('could not get user permissions');
+    }
+
+    var permissions = [];
+    if (data.data) {
+      var perms = data.data;
+      var count = 0;
+
+      for (var i in perms) {
+        count++;
+        var perm = perms[i];
+        var parts = perm.split(':');
+        var ops_part = "";
+        var path_part = parts[0];
+
+        if (parts.length > 1) {
+          ops_part = parts[0];
+          path_part = parts[1];
+        }
+
+        ops_part=ops_part.replace("*", "get,post,put,delete");
+        var ops = ops_part.split(',');
+        var ops_object = {};
+        ops_object.get = 'no';
+        ops_object.post = 'no';
+        ops_object.put = 'no';
+        ops_object.delete = 'no';
+        for (var j in ops) {
+          ops_object[ops[j]] = 'yes';
+        }
+
+        permissions.push({
+          operations: ops_object,
+          path: path_part,
+          perm: perm
+        });
+      }
+    }
+
+    self.permissions = permissions;
+
+    doCallback(callback, [err, data, data.entities], self);
+  });
+
+};
diff --git a/lib/modules/Error.js b/lib/modules/Error.js
new file mode 100644
index 0000000..5cc5807
--- /dev/null
+++ b/lib/modules/Error.js
@@ -0,0 +1,155 @@
+/*
+ *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
+
+/**
+ * Created by ryan bridges on 2014-02-05.
+ */
+(function(global) {
+    //noinspection JSUnusedAssignment
+    var name = 'UsergridError',
+        short,
+        _name = global[name],
+        _short = (short && short !== undefined) ? global[short] : undefined;
+
+    /*
+     *  Instantiates a new UsergridError
+     *
+     *  @method UsergridError
+     *  @public
+     *  @params {<string>} message
+     *  @params {<string>} id       - the error code, id, or name
+     *  @params {<int>} timestamp
+     *  @params {<int>} duration
+     *  @params {<string>} exception    - the Java exception from Usergrid
+     *  @return Returns - a new UsergridError object
+     *
+     *  Example:
+     *
+     *  UsergridError(message);
+     */
+
+    function UsergridError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name;
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridError.prototype = new Error();
+    UsergridError.prototype.constructor = UsergridError;
+    /*
+     *  Creates a UsergridError from the JSON response returned from the backend
+     *
+     *  @method fromResponse
+     *  @public
+     *  @params {object} response - the deserialized HTTP response from the Usergrid API
+     *  @return Returns a new UsergridError object.
+     *
+     *  Example:
+     *  {
+     *  "error":"organization_application_not_found",
+     *  "timestamp":1391618508079,
+     *  "duration":0,
+     *  "exception":"org.usergrid.rest.exceptions.OrganizationApplicationNotFoundException",
+     *  "error_description":"Could not find application for yourorgname/sandboxxxxx from URI: yourorgname/sandboxxxxx"
+     *  }
+     */
+    UsergridError.fromResponse = function(response) {
+        if (response && "undefined" !== typeof response) {
+            return new UsergridError(response.error_description, response.error, response.timestamp, response.duration, response.exception);
+        } else {
+            return new UsergridError();
+        }
+    };
+    UsergridError.createSubClass = function(name) {
+        if (name in global && global[name]) return global[name];
+        global[name] = function() {};
+        global[name].name = name;
+        global[name].prototype = new UsergridError();
+        return global[name];
+    };
+
+    function UsergridHTTPResponseError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name;
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridHTTPResponseError.prototype = new UsergridError();
+
+    function UsergridInvalidHTTPMethodError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name || 'invalid_http_method';
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridInvalidHTTPMethodError.prototype = new UsergridError();
+
+    function UsergridInvalidURIError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name || 'invalid_uri';
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridInvalidURIError.prototype = new UsergridError();
+
+    function UsergridInvalidArgumentError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name || 'invalid_argument';
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridInvalidArgumentError.prototype = new UsergridError();
+
+    function UsergridKeystoreDatabaseUpgradeNeededError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name;
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridKeystoreDatabaseUpgradeNeededError.prototype = new UsergridError();
+
+    global.UsergridHTTPResponseError = UsergridHTTPResponseError;
+    global.UsergridInvalidHTTPMethodError = UsergridInvalidHTTPMethodError;
+    global.UsergridInvalidURIError = UsergridInvalidURIError;
+    global.UsergridInvalidArgumentError = UsergridInvalidArgumentError;
+    global.UsergridKeystoreDatabaseUpgradeNeededError = UsergridKeystoreDatabaseUpgradeNeededError;
+
+    global[name] = UsergridError;
+    if (short !== undefined) {
+        //noinspection JSUnusedAssignment
+        global[short] = UsergridError;
+    }
+    global[name].noConflict = function() {
+        if (_name) {
+            global[name] = _name;
+        }
+        if (short !== undefined) {
+            global[short] = _short;
+        }
+        return UsergridError;
+    };
+    return global[name];
+}(this));
diff --git a/lib/modules/Folder.js b/lib/modules/Folder.js
new file mode 100644
index 0000000..9d13155
--- /dev/null
+++ b/lib/modules/Folder.js
@@ -0,0 +1,190 @@
+/*
+ *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.
+ */
+
+/*
+ *  A class to model a Usergrid folder.
+ *
+ *  @constructor
+ *  @param {object} options {name:"MyPhotos", path:"/user/uploads", owner:"00000000-0000-0000-0000-000000000000" }
+ *  @returns {callback} callback(err, folder)
+ */
+Usergrid.Folder = function(options, callback) {
+	var self = this,
+		messages = [];
+	console.log("FOLDER OPTIONS", options);
+	self._client = options.client;
+	self._data = options.data || {};
+	self._data.type = "folders";
+	var missingData = ["name", "owner", "path"].some(function(required) {
+		return !(required in self._data);
+	});
+	if (missingData) {
+		return doCallback(callback, [new UsergridInvalidArgumentError("Invalid asset data: 'name', 'owner', and 'path' are required properties."), null, self], self);
+	}
+	self.save(function(err, response) {
+		if (err) {
+			doCallback(callback, [new UsergridError(response), response, self], self);
+		} else {
+			if (response && response.entities && response.entities.length) {
+				self.set(response.entities[0]);
+			}
+			doCallback(callback, [null, response, self], self);
+		}
+	});
+};
+/*
+ *  Inherit from Usergrid.Entity.
+ */
+Usergrid.Folder.prototype = new Usergrid.Entity();
+
+
+/*
+ *  fetch the folder and associated assets
+ *
+ *  @method fetch
+ *  @public
+ *  @param {function} callback(err, self)
+ *  @returns {callback} callback(err, self)
+ */
+Usergrid.Folder.prototype.fetch = function(callback) {
+	var self = this;
+	Usergrid.Entity.prototype.fetch.call(self, function(err, data) {
+		console.log("self", self.get());
+		console.log("data", data);
+		if (!err) {
+			self.getAssets(function(err, response) {
+				if (err) {
+					doCallback(callback, [new UsergridError(response), resonse, self], self);
+				} else {
+					doCallback(callback, [null, self], self);
+				}
+			});
+		} else {
+			doCallback(callback, [null, data, self], self);
+		}
+	});
+};
+/*
+ *  Add an asset to the folder.
+ *
+ *  @method addAsset
+ *  @public
+ *  @param {object} options {asset:(uuid || Usergrid.Asset || {name:"photo.jpg", path:"/user/uploads", "content-type":"image/jpeg", owner:"F01DE600-0000-0000-0000-000000000000" }) }
+ *  @returns {callback} callback(err, folder)
+ */
+Usergrid.Folder.prototype.addAsset = function(options, callback) {
+	var self = this;
+	if (('asset' in options)) {
+		var asset = null;
+		switch (typeof options.asset) {
+			case 'object':
+				asset = options.asset;
+				if (!(asset instanceof Usergrid.Entity)) {
+					asset = new Usergrid.Asset(asset);
+				}
+				break;
+			case 'string':
+				if (isUUID(options.asset)) {
+					asset = new Usergrid.Asset({
+						client: self._client,
+						data: {
+							uuid: options.asset,
+							type: "assets"
+						}
+					});
+				}
+				break;
+		}
+		if (asset && asset instanceof Usergrid.Entity) {
+			asset.fetch(function(err, data) {
+				if (err) {
+					doCallback(callback, [new UsergridError(data), data, self], self);
+				} else {
+					var endpoint = ["folders", self.get("uuid"), "assets", asset.get("uuid")].join('/');
+					var options = {
+						method: 'POST',
+						endpoint: endpoint
+					};
+					self._client.request(options, callback);
+				}
+			});
+		}
+	} else {
+		//nothing to add
+		doCallback(callback, [new UsergridInvalidArgumentError("No asset specified"), null, self], self);
+	}
+};
+
+/*
+ *  Remove an asset from the folder.
+ *
+ *  @method removeAsset
+ *  @public
+ *  @param {object} options {asset:(uuid || Usergrid.Asset || {name:"photo.jpg", path:"/user/uploads", "content-type":"image/jpeg", owner:"F01DE600-0000-0000-0000-000000000000" }) }
+ *  @returns {callback} callback(err, folder)
+ */
+Usergrid.Folder.prototype.removeAsset = function(options, callback) {
+	var self = this;
+	if (('asset' in options)) {
+		var asset = null;
+		switch (typeof options.asset) {
+			case 'object':
+				asset = options.asset;
+				break;
+			case 'string':
+				if (isUUID(options.asset)) {
+					asset = new Usergrid.Asset({
+						client: self._client,
+						data: {
+							uuid: options.asset,
+							type: "assets"
+						}
+					});
+				}
+				break;
+		}
+		if (asset && asset !== null) {
+			var endpoint = ["folders", self.get("uuid"), "assets", asset.get("uuid")].join('/');
+			self._client.request({
+				method: 'DELETE',
+				endpoint: endpoint
+			}, function(err, response) {
+				if (err) {
+					doCallback(callback, [new UsergridError(response), response, self], self);
+				} else {
+					doCallback(callback, [null, response, self], self);
+				}
+			});
+		}
+	} else {
+		//nothing to add
+		doCallback(callback, [new UsergridInvalidArgumentError("No asset specified"), null, self], self);
+	}
+};
+
+/*
+ *  List the assets in the folder.
+ *
+ *  @method getAssets
+ *  @public
+ *  @returns {callback} callback(err, assets)
+ */
+Usergrid.Folder.prototype.getAssets = function(callback) {
+	return this.getConnections("assets", callback);
+};
diff --git a/lib/modules/Group.js b/lib/modules/Group.js
new file mode 100644
index 0000000..1ea27c2
--- /dev/null
+++ b/lib/modules/Group.js
@@ -0,0 +1,231 @@
+/*
+ *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.
+ */
+
+
+/*
+ *  A class to model a Usergrid group.
+ *  Set the path in the options object.
+ *
+ *  @constructor
+ *  @param {object} options {client:client, data: {'key': 'value'}, path:'path'}
+ */
+Usergrid.Group = function(options, callback) {
+  this._path = options.path;
+  this._list = [];
+  this._client = options.client;
+  this._data = options.data || {};
+  this._data.type = "groups";
+};
+
+/*
+ *  Inherit from Usergrid.Entity.
+ *  Note: This only accounts for data on the group object itself.
+ *  You need to use add and remove to manipulate group membership.
+ */
+Usergrid.Group.prototype = new Usergrid.Entity();
+
+/*
+ *  Fetches current group data, and members.
+ *
+ *  @method fetch
+ *  @public
+ *  @param {function} callback
+ *  @returns {function} callback(err, data)
+ */
+Usergrid.Group.prototype.fetch = function(callback) {
+  var self = this;
+  var groupEndpoint = 'groups/' + this._path;
+  var memberEndpoint = 'groups/' + this._path + '/users';
+
+  var groupOptions = {
+    method: 'GET',
+    endpoint: groupEndpoint
+  };
+
+  var memberOptions = {
+    method: 'GET',
+    endpoint: memberEndpoint
+  };
+
+  this._client.request(groupOptions, function(err, response) {
+    if (err) {
+      if (self._client.logging) {
+        console.log('error getting group');
+      }
+      doCallback(callback, [err, response], self);
+    } else {
+      var entities = response.getEntities();
+      if (entities && entities.length) {
+        var groupresponse = entities.shift();
+        //self._response = groupresponse || {};
+        self._client.request(memberOptions, function(err, response) {
+          if (err && self._client.logging) {
+            console.log('error getting group users');
+          } else {
+            self._list = response.getEntities()
+              .filter(function(entity) {
+                return isUUID(entity.uuid);
+              })
+              .map(function(entity) {
+                return new Usergrid.Entity({
+                  type: entity.type,
+                  client: self._client,
+                  uuid: entity.uuid,
+                  response: entity //TODO: deprecate this property
+                });
+              });
+          }
+          doCallback(callback, [err, response, self], self);
+        });
+      }
+    }
+  });
+};
+
+/*
+ *  Retrieves the members of a group.
+ *
+ *  @method members
+ *  @public
+ *  @param {function} callback
+ *  @return {function} callback(err, data);
+ */
+Usergrid.Group.prototype.members = function(callback) {
+  //doCallback(callback, [null, this._list, this], this);
+  return this._list;
+};
+
+/*
+ *  Adds an existing user to the group, and refreshes the group object.
+ *
+ *  Options object: {user: user_entity}
+ *
+ *  @method add
+ *  @public
+ *  @params {object} options
+ *  @param {function} callback
+ *  @return {function} callback(err, data)
+ */
+Usergrid.Group.prototype.add = function(options, callback) {
+  var self = this;
+  if (options.user) {
+    options = {
+      method: "POST",
+      endpoint: "groups/" + this._path + "/users/" + options.user.get('username')
+    };
+    this._client.request(options, function(error, response) {
+      if (error) {
+        doCallback(callback, [error, response, self], self);
+      } else {
+        self.fetch(callback);
+      }
+    });
+  } else {
+    doCallback(callback, [new UsergridError("no user specified", 'no_user_specified'), null, this], this);
+  }
+};
+
+/*
+ *  Removes a user from a group, and refreshes the group object.
+ *
+ *  Options object: {user: user_entity}
+ *
+ *  @method remove
+ *  @public
+ *  @params {object} options
+ *  @param {function} callback
+ *  @return {function} callback(err, data)
+ */
+Usergrid.Group.prototype.remove = function(options, callback) {
+  var self = this;
+  if (options.user) {
+    options = {
+      method: "DELETE",
+      endpoint: "groups/" + this._path + "/users/" + options.user.username
+    };
+    this._client.request(options, function(error, response) {
+      if (error) {
+        doCallback(callback, [error, response, self], self);
+      } else {
+        self.fetch(callback);
+      }
+    });
+  } else {
+    doCallback(callback, [new UsergridError("no user specified", 'no_user_specified'), null, this], this);
+  }
+};
+
+/*
+ * Gets feed for a group.
+ *
+ * @public
+ * @method feed
+ * @param {function} callback
+ * @returns {callback} callback(err, data, activities)
+ */
+Usergrid.Group.prototype.feed = function(callback) {
+  var self = this;
+  var options = {
+    method: "GET",
+    endpoint: "groups/" + this._path + "/feed"
+  };
+  this._client.request(options, function(err, response) {
+    doCallback(callback, [err, response, self], self);
+  });
+};
+
+/*
+ * Creates activity and posts to group feed.
+ *
+ * options object: {user: user_entity, content: "activity content"}
+ *
+ * @public
+ * @method createGroupActivity
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, entity)
+ */
+Usergrid.Group.prototype.createGroupActivity = function(options, callback) {
+  var self = this;
+  var user = options.user;
+  var entity = new Usergrid.Entity({
+    client: this._client,
+    data: {
+      actor: {
+        "displayName": user.get("username"),
+        "uuid": user.get("uuid"),
+        "username": user.get("username"),
+        "email": user.get("email"),
+        "picture": user.get("picture"),
+        "image": {
+          "duration": 0,
+          "height": 80,
+          "url": user.get("picture"),
+          "width": 80
+        },
+      },
+      "verb": "post",
+      "content": options.content,
+      "type": 'groups/' + this._path + '/activities'
+    }
+  });
+  entity.save(function(err, response, entity) {
+    doCallback(callback, [err, response, self]);
+  });
+};
diff --git a/lib/modules/util/Ajax.js b/lib/modules/util/Ajax.js
new file mode 100644
index 0000000..4b8a381
--- /dev/null
+++ b/lib/modules/util/Ajax.js
@@ -0,0 +1,99 @@
+/*
+ *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.
+ *
+ *  @author ryan bridges (rbridges@apigee.com)
+ */
+
+//Ajax
+(function() {
+    var name = 'Ajax', global = this, overwrittenName = global[name], exports;
+
+    function partial(){
+        var args = Array.prototype.slice.call(arguments);
+        var fn=args.shift();
+        return fn.bind(this, args);
+    }
+    function Ajax() {
+        this.logger=new global.Logger(name);
+        var self=this;
+        function encode(data) {
+            var result = "";
+            if (typeof data === "string") {
+                result = data;
+            } else {
+                var e = encodeURIComponent;
+                for (var i in data) {
+                    if (data.hasOwnProperty(i)) {
+                        result += '&' + e(i) + '=' + e(data[i]);
+                    }
+                }
+            }
+            return result;
+        }
+        function request(m, u, d) {
+            var p = new Promise(), timeout;
+            self.logger.time(m + ' ' + u);
+            (function(xhr) {
+                xhr.onreadystatechange = function() {
+                    if(this.readyState === 4){
+                        self.logger.timeEnd(m + ' ' + u);
+                        clearTimeout(timeout);
+                        p.done(null, this);
+                    }
+                };
+                xhr.onerror=function(response){
+                    clearTimeout(timeout);
+                    p.done(response, null);
+                };
+                xhr.oncomplete=function(response){
+                    clearTimeout(timeout);
+                    self.logger.timeEnd(m + ' ' + u);
+                    self.info("%s request to %s returned %s", m, u, this.status );
+                };
+                xhr.open(m, u);
+                if (d) {
+                    if("object"===typeof d){
+                        d=JSON.stringify(d);
+                    }
+                    xhr.setRequestHeader("Content-Type", "application/json");
+                    xhr.setRequestHeader("Accept", "application/json");
+                }
+                timeout = setTimeout(function() {
+                    xhr.abort();
+                    p.done("API Call timed out.", null);
+                }, 30000);
+                //TODO stick that timeout in a config variable
+                xhr.send(encode(d));
+            }(new XMLHttpRequest()));
+            return p;
+        }
+        this.request=request;
+        this.get = partial(request,'GET');
+        this.post = partial(request,'POST');
+        this.put = partial(request,'PUT');
+        this.delete = partial(request,'DELETE');
+    }
+    global[name] =  new Ajax();
+    global[name].noConflict = function() {
+        if(overwrittenName){
+            global[name] = overwrittenName;
+        }
+        return exports;
+    };
+    return global[name];
+}());
diff --git a/lib/modules/util/Event.js b/lib/modules/util/Event.js
new file mode 100644
index 0000000..d826e04
--- /dev/null
+++ b/lib/modules/util/Event.js
@@ -0,0 +1,33 @@
+var UsergridEventable	= function(){
+    throw Error("'UsergridEventable' is not intended to be invoked directly");
+};
+UsergridEventable.prototype	= {
+    bind	: function(event, fn){
+        this._events = this._events || {};
+        this._events[event] = this._events[event]	|| [];
+        this._events[event].push(fn);
+    },
+    unbind	: function(event, fn){
+        this._events = this._events || {};
+        if( event in this._events === false  )	return;
+        this._events[event].splice(this._events[event].indexOf(fn), 1);
+    },
+    trigger	: function(event /* , args... */){
+        this._events = this._events || {};
+        if( event in this._events === false  )	return;
+        for(var i = 0; i < this._events[event].length; i++){
+            this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
+        }
+    }
+};
+UsergridEventable.mixin	= function(destObject){
+    var props	= ['bind', 'unbind', 'trigger'];
+    for(var i = 0; i < props.length; i ++){
+        if(props[i] in destObject.prototype){
+            console.warn("overwriting '"+props[i]+"' on '"+destObject.name+"'.");
+            console.warn("the previous version can be found at '_"+props[i]+"' on '"+destObject.name+"'.");
+            destObject.prototype['_'+props[i]]=destObject.prototype[props[i]];
+        }
+        destObject.prototype[props[i]]	= UsergridEventable.prototype[props[i]];
+    }
+};
diff --git a/lib/modules/util/Logger.js b/lib/modules/util/Logger.js
new file mode 100644
index 0000000..c3cf579
--- /dev/null
+++ b/lib/modules/util/Logger.js
@@ -0,0 +1,89 @@
+/*
+ *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.
+ *
+ *  @author ryan bridges (rbridges@apigee.com)
+ */
+
+//Logger
+(function() {
+    var name = 'Logger', global = this, overwrittenName = global[name], exports;
+    /* logging */
+    function Logger(name) {
+        this.logEnabled = true;
+        this.init(name, true);
+    }
+    Logger.METHODS=[
+        "log", "error", "warn", "info", "debug", "assert", "clear", "count",
+        "dir", "dirxml", "exception", "group", "groupCollapsed", "groupEnd",
+        "profile", "profileEnd", "table", "time", "timeEnd", "trace"
+    ];
+    Logger.prototype.init=function(name, logEnabled){
+        this.name=name||"UNKNOWN";
+        this.logEnabled=logEnabled||true;
+        var addMethod=function(method){this[method]=this.createLogMethod(method);}.bind(this);
+        Logger.METHODS.forEach(addMethod);
+    };
+    Logger.prototype.createLogMethod=function(method){
+        return Logger.prototype.log.bind(this, method);
+    };
+    Logger.prototype.prefix=function(method, args){
+        var prepend='['+method.toUpperCase()+']['+name+"]:\t";
+        if(['log', 'error', 'warn', 'info'].indexOf(method)!==-1){
+            if("string"===typeof args[0]){
+                args[0]=prepend+args[0];
+            }else{
+                args.unshift(prepend);
+            }
+        }
+        return args;
+    };
+    Logger.prototype.log=function(){
+        var args=[].slice.call(arguments);
+        method=args.shift();
+        if(Logger.METHODS.indexOf(method)===-1){
+            method="log";
+        }
+        if(!(this.logEnabled && console && console[method]))return;
+        args=this.prefix(method, args);
+        console[method].apply(console, args);
+    };
+    Logger.prototype.setLogEnabled=function(logEnabled){
+        this.logEnabled=logEnabled||true;
+    };
+
+    Logger.mixin	= function(destObject){
+        destObject.__logger=new Logger(destObject.name||"UNKNOWN");
+        var addMethod=function(method){
+            if(method in destObject.prototype){
+                console.warn("overwriting '"+method+"' on '"+destObject.name+"'.");
+                console.warn("the previous version can be found at '_"+method+"' on '"+destObject.name+"'.");
+                destObject.prototype['_'+method]=destObject.prototype[method];
+            }
+            destObject.prototype[method]=destObject.__logger.createLogMethod(method);
+        };
+        Logger.METHODS.forEach(addMethod);
+    };
+    global[name] =  Logger;
+    global[name].noConflict = function() {
+        if(overwrittenName){
+            global[name] = overwrittenName;
+        }
+        return Logger;
+    };
+    return global[name];
+}());
diff --git a/lib/modules/util/Promise.js b/lib/modules/util/Promise.js
new file mode 100644
index 0000000..76d0d04
--- /dev/null
+++ b/lib/modules/util/Promise.js
@@ -0,0 +1,101 @@
+/*
+ *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.
+ *
+ *  @author ryan bridges (rbridges@apigee.com)
+ */
+
+//Promise
+(function(global) {
+    var name = 'Promise', overwrittenName = global[name], exports;
+
+        function Promise() {
+            this.complete = false;
+            this.error = null;
+            this.result = null;
+            this.callbacks = [];
+        }
+        Promise.prototype.then = function(callback, context) {
+            var f = function() {
+                return callback.apply(context, arguments);
+            };
+            if (this.complete) {
+                f(this.error, this.result);
+            } else {
+                this.callbacks.push(f);
+            }
+        };
+        Promise.prototype.done = function(error, result) {
+            this.complete = true;
+            this.error = error;
+            this.result = result;
+            if(this.callbacks){
+                for (var i = 0; i < this.callbacks.length; i++) this.callbacks[i](error, result);
+                this.callbacks.length = 0;
+            }
+        };
+        Promise.join = function(promises) {
+            var p = new Promise(),
+                total = promises.length,
+                completed = 0,
+                errors = [],
+                results = [];
+
+            function notifier(i) {
+                return function(error, result) {
+                    completed += 1;
+                    errors[i] = error;
+                    results[i] = result;
+                    if (completed === total) {
+                        p.done(errors, results);
+                    }
+                };
+            }
+            for (var i = 0; i < total; i++) {
+                promises[i]().then(notifier(i));
+            }
+            return p;
+        };
+        Promise.chain = function(promises, error, result) {
+            var p = new Promise();
+            if (promises===null||promises.length === 0) {
+                p.done(error, result);
+            } else {
+                promises[0](error, result).then(function(res, err) {
+                    promises.splice(0, 1);
+                    //self.logger.info(promises.length)
+                    if(promises){
+                        Promise.chain(promises, res, err).then(function(r, e) {
+                            p.done(r, e);
+                        });
+                    }else{
+                        p.done(res, err);
+                    }
+                });
+            }
+            return p;
+        };
+
+    global[name] =  Promise;
+    global[name].noConflict = function() {
+        if(overwrittenName){
+            global[name] = overwrittenName;
+        }
+        return Promise;
+    };
+    return global[name];
+}(this));
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..72e2046
--- /dev/null
+++ b/package.json
@@ -0,0 +1,22 @@
+{
+  "name": "usergrid",
+  "version": "0.11.0",
+  "description": "Detailed instructions follow but if you just want a quick example of how to get started with this SDK, here’s a minimal HTML5 file that shows you how to include & initialize the SDK, as well as how to read & write data from Usergrid with it.",
+  "main": "usergrid.js",
+  "directories": {
+    "example": "examples"
+  },
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "",
+  "license": "Apache 2.0",
+  "devDependencies": {
+    "grunt": "~0.4.2",
+    "grunt-contrib-clean": "~0.5.0",
+    "grunt-contrib-watch": "~0.5.3",
+    "grunt-contrib-uglify": "~0.2.7",
+    "grunt-blanket-mocha": "~0.3.3",
+    "grunt-contrib-connect": "~0.6.0"
+  }
+}
diff --git a/tests/mocha/index.html b/tests/mocha/index.html
new file mode 100644
index 0000000..da47564
--- /dev/null
+++ b/tests/mocha/index.html
@@ -0,0 +1,60 @@
+
+<!--
+  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.
+-->
+
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Mocha Tests</title>
+  <link rel="stylesheet" href="../resources/css/mocha.css" />
+  <script src="../resources/js/mocha.js"></script>
+  <script>mocha.setup('bdd')</script>
+  <script src="../resources/js/blanket_mocha.min.js"></script>
+  <script type="text/javascript" src="../../node_modules/grunt-blanket-mocha/support/mocha-blanket.js"></script>
+	<script>
+      function assert(expr, msg) {
+        if (!expr) throw new Error(msg || 'failed');
+      }
+    </script>
+
+  <script src="../../usergrid.js" data-cover></script>
+  <script src="test.js"></script>
+<!-- run mocha -->
+    <script type="text/javascript" charset="utf-8">
+    	var _onload=onload||function(){};
+        onload = function(){
+        	_onload.apply(this, arguments);
+	        mocha.checkLeaks();
+	        mocha.globals(['']);
+	        var runner = mocha.run();
+	        /*runner.on('test end', function(test){
+	          console.log(test.fullTitle());
+	        });*/
+	      };
+    </script>
+
+  <script>
+    if (window.PHANTOMJS) {
+        blanket.options("reporter", "../../node_modules/grunt-blanket-mocha/support/grunt-reporter.js");
+    }
+  </script>
+
+</head>
+<body>
+	<div id="mocha"></div>
+</body>
+</html>
diff --git a/tests/mocha/test.js b/tests/mocha/test.js
new file mode 100644
index 0000000..ef4ff54
--- /dev/null
+++ b/tests/mocha/test.js
@@ -0,0 +1,1078 @@
+//
+// 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.
+//
+
+/*
+	Creates a generic usergrid client with logging and buildCurl disabled
+
+ */
+function getClient() {
+	return new Usergrid.Client({
+		orgName: 'yourorgname',
+		appName: 'sandbox',
+		logging: false, //optional - turn on logging, off by default
+		buildCurl: true //optional - turn on curl commands, off by default
+	});
+}
+/*
+	A convenience function that will test for the presence of an API error
+	and run any number of additional tests
+ */
+function usergridTestHarness(err, data, done, tests, ignoreError) {
+	if (!ignoreError) assert(!err, (err)?err.error_description:"unknown");
+	if (tests) {
+		if ("function" === typeof tests) {
+			tests(err, data);
+		} else if (tests.length) {
+			tests.forEach(function(test) {
+				if ("function" === typeof test) {
+					test(err, data);
+				}
+			})
+		}
+	}
+	done();
+}
+describe('Ajax', function() {
+    var dogName="dog"+Math.floor(Math.random()*10000);
+    var dogData=JSON.stringify({type:"dog",name:dogName});
+    var dogURI='https://api.usergrid.com/yourorgname/sandbox/dogs'
+    it('should POST to a URI',function(done){
+        Ajax.post(dogURI, dogData).then(function(err, data){
+            assert(!err, err);
+            done();
+        })
+    })
+    it('should GET a URI',function(done){
+        Ajax.get(dogURI+'/'+dogName).then(function(err, data){
+            assert(!err, err);
+            done();
+        })
+    })
+    it('should PUT to a URI',function(done){
+        Ajax.put(dogURI+'/'+dogName, {"favorite":true}).then(function(err, data){
+            assert(!err, err);
+            done();
+        })
+    })
+    it('should DELETE a URI',function(done){
+        Ajax.delete(dogURI+'/'+dogName, dogData).then(function(err, data){
+            assert(!err, err);
+            done();
+        })
+    })
+});
+describe('UsergridError', function() {
+    var errorResponse={
+        "error":"service_resource_not_found",
+        "timestamp":1392067967144,
+        "duration":0,
+        "exception":"org.usergrid.services.exceptions.ServiceResourceNotFoundException",
+        "error_description":"Service resource not found"
+    };
+    it('should unmarshal a response from Usergrid into a proper Javascript error',function(done){
+        var error = UsergridError.fromResponse(errorResponse);
+        assert(error.name===errorResponse.error, "Error name not set correctly");
+        assert(error.message===errorResponse.error_description, "Error message not set correctly");
+        done();
+    });
+});
+describe('Usergrid', function(){
+    describe('SDK Version', function(){
+        it('should contain a minimum SDK version',function(){
+            var parts=Usergrid.VERSION.split('.').map(function(i){return i.replace(/^0+/,'')}).map(function(i){return parseInt(i)});
+
+            assert(parts[1]>=10, "expected minor version >=10");
+            assert(parts[1]>10||parts[2]>=8, "expected minimum version >=8");
+        });
+    });
+    describe('Usergrid Request/Response', function() {
+        var dogName="dog"+Math.floor(Math.random()*10000);
+        var dogData=JSON.stringify({type:"dog",name:dogName});
+        var dogURI='https://api.usergrid.com/yourorgname/sandbox/dogs'
+        it('should POST to a URI',function(done){
+            var req=new Usergrid.Request("POST", dogURI, {}, dogData, function(err, response){
+                console.error(err, response);
+                assert(!err, err);
+                assert(response instanceof Usergrid.Response, "Response is not and instance of Usergrid.Response");
+                done();
+            })
+        })
+        it('should GET a URI',function(done){
+            var req=new Usergrid.Request("GET", dogURI+'/'+dogName, {}, null, function(err, response){
+                assert(!err, err);
+                assert(response instanceof Usergrid.Response, "Response is not and instance of Usergrid.Response");
+                done();
+            })
+        })
+        it('should GET an array of entity data from the Usergrid.Response object',function(done){
+            var req=new Usergrid.Request("GET", dogURI, {}, null, function(err, response){
+                assert(!err, err);
+                assert(response instanceof Usergrid.Response, "Response is not and instance of Usergrid.Response");
+                var entities=response.getEntities();
+                assert(entities && entities.length, "Nothing was returned")
+                done();
+            })
+        })
+        it('should GET entity data from the Usergrid.Response object',function(done){
+            var req=new Usergrid.Request("GET", dogURI+'/'+dogName, {}, null, function(err, response){
+                var entity=response.getEntity();
+                assert(!err, err);
+                assert(response instanceof Usergrid.Response, "Response is not and instance of Usergrid.Response");
+                assert(entity, "Nothing was returned")
+                done();
+            })
+        })
+        it('should PUT to a URI',function(done){
+            var req=new Usergrid.Request("PUT", dogURI+'/'+dogName, {}, {favorite:true}, function(err, response){
+                assert(!err, err);
+                assert(response instanceof Usergrid.Response, "Response is not and instance of Usergrid.Response");
+                done();
+            })
+        })
+        it('should DELETE a URI',function(done){
+            var req=new Usergrid.Request("DELETE", dogURI+'/'+dogName, {}, null, function(err, response){
+                assert(!err, err);
+                assert(response instanceof Usergrid.Response, "Response is not and instance of Usergrid.Response");
+                done();
+            })
+        })
+        it('should NOT allow an invalid method',function(done){
+            try{
+                var req=new Usergrid.Request("INVALID", dogURI+'/'+dogName, {}, null, function(err, response){
+                    assert(true, "Should have thrown an UsergridInvalidHTTPMethodError");
+                    done();
+                })
+            }catch(e){
+                assert(e instanceof UsergridInvalidHTTPMethodError, "Error is not and instance of UsergridInvalidHTTPMethodError");
+                done()
+            }
+        })
+        it('should NOT allow an invalid URI',function(done){
+            try{
+                var req=new Usergrid.Request("GET", "://apigee.com", {}, null, function(err, response){
+                    assert(true, "Should have thrown an UsergridInvalidURIError");
+                    done();
+                })
+            }catch(e){
+                assert(e instanceof UsergridInvalidURIError, "Error is not and instance of UsergridInvalidURIError");
+                done()
+            }
+        })
+        it('should return a UsergridError object on an invalid URI',function(done){
+            var req=new Usergrid.Request("GET", dogURI+'/'+dogName+'zzzzz', {}, null, function(err, response){
+                assert(err, "Should have returned an error");
+                assert(response instanceof Usergrid.Response, "Response is not and instance of Usergrid.Response");
+                assert(err instanceof UsergridError, "Error is not and instance of UsergridError");
+                done();
+            })
+        })
+    });
+    describe('Usergrid Client', function() {
+        var client = getClient();
+        describe('Usergrid CRUD request', function() {
+            before(function(done) {
+                this.timeout(10000);
+                //Make sure our dog doesn't already exist
+                client.request({
+                    method: 'DELETE',
+                    endpoint: 'users/fred'
+                }, function(err, data) {
+                    done();
+                });
+            });
+            var options = {
+                method: 'GET',
+                endpoint: 'users'
+            };
+            it('should persist default query parameters', function(done) {
+                //create new client with default params
+                var client=new Usergrid.Client({
+                    orgName: 'yourorgname',
+                    appName: 'sandbox',
+                    logging: false, //optional - turn on logging, off by default
+                    buildCurl: true, //optional - turn on curl commands, off by default
+                    qs:{
+                        test1:'test1',
+                        test2:'test2'
+                    }
+                });
+                var default_qs=client.getObject('default_qs');
+                assert(default_qs.test1==='test1', "the default query parameters were not persisted");
+                assert(default_qs.test2==='test2', "the default query parameters were not persisted");
+                client.request({
+                    method: 'GET',
+                    endpoint: 'users'
+                }, function(err, data) {
+                    assert(data.params.test2[0]==='test2', "the default query parameters were not sent to the backend");
+                    assert(data.params.test1[0]==='test1', "the default query parameters were not sent to the backend");
+                    done();
+                });
+            });
+            it('should CREATE a new user', function(done) {
+                client.request({
+                    method: 'POST',
+                    endpoint: 'users',
+                    body: {
+                        username: 'fred',
+                        password: 'secret'
+                    }
+                }, function(err, data) {
+                    usergridTestHarness(err, data, done, [
+                        function(err, data) {
+                            assert(!err)
+                        }
+                    ]);
+                });
+            });
+            it('should RETRIEVE an existing user', function(done) {
+                client.request({
+                    method: 'GET',
+                    endpoint: 'users/fred',
+                    body: {}
+                }, function(err, data) {
+                    usergridTestHarness(err, data, done, [
+
+                        function(err, data) {
+                            assert(true)
+                        }
+                    ]);
+                });
+            });
+            it('should UPDATE an existing user', function(done) {
+                client.request({
+                    method: 'PUT',
+                    endpoint: 'users/fred',
+                    body: {
+                        newkey: 'newvalue'
+                    }
+                }, function(err, data) {
+                    usergridTestHarness(err, data, done, [
+
+                        function(err, data) {
+                            assert(true)
+                        }
+                    ]);
+                });
+            });
+            it('should DELETE the user from the database', function(done) {
+                client.request({
+                    method: 'DELETE',
+                    endpoint: 'users/fred'
+                }, function(err, data) {
+                    usergridTestHarness(err, data, done, [
+
+                        function(err, data) {
+                            assert(true)
+                        }
+                    ]);
+                });
+            });
+        });
+        describe('Usergrid REST', function() {
+            it('should return a list of users', function(done) {
+                client.request({
+                    method: 'GET',
+                    endpoint: 'users'
+                }, function(err, data) {
+                    usergridTestHarness(err, data, done, [
+                        function(err, data) {
+                            assert(data.entities.length>=0, "Request should return at least one user");
+                        }
+                    ]);
+                });
+            });
+            it('should return no entities when an endpoint does not exist', function(done) {
+                client.request({
+                    method: 'GET',
+                    endpoint: 'nonexistantendpoint'
+                }, function(err, data) {
+                    usergridTestHarness(err, data, done, [
+
+                        function(err, data) {
+                            assert(data.entities.length===0, "Request should return no entities");
+                        }
+                    ]);
+                });
+            });
+        });
+        describe('Usergrid convenience methods', function(){
+            before(function(){ client.logout();});
+            it('createEntity',function(done){
+                client.createEntity({type:'dog',name:'createEntityTestDog'}, function(err, response, dog){
+                    console.warn(err, response, dog);
+                    assert(!err, "createEntity returned an error")
+                    assert(dog, "createEntity did not return a dog")
+                    assert(dog.get("name")==='createEntityTestDog', "The dog's name is not 'createEntityTestDog'")
+                    done();
+                })
+            })
+            it('createEntity - existing entity',function(done){
+                    client.createEntity({type:'dog',name:'createEntityTestDog'}, function(err, response, dog){
+                        try{
+                            assert(err, "createEntity should return an error")
+                        }catch(e){
+                            assert(true, "trying to create an entity that already exists throws an error");
+                        }finally{
+                            done();
+                        }
+                    });
+            })
+            var testGroup;
+            it('createGroup',function(done){
+                client.createGroup({path:'dogLovers'},function(err, response, group){
+                        try{
+                            assert(!err, "createGroup returned an error")
+                        }catch(e){
+                            assert(true, "trying to create a group that already exists throws an error");
+                        }finally{
+                            done();
+                        }
+                    /*assert(!err, "createGroup returned an error: "+err);
+                    assert(group, "createGroup did not return a group");
+                    assert(group instanceof Usergrid.Group, "createGroup did not return a Usergrid.Group");
+                    testGroup=group;
+                    done();*/
+                })
+                done();
+            })
+            it('buildAssetURL',function(done){
+                var assetURL='https://api.usergrid.com/yourorgname/sandbox/assets/00000000-0000-0000-000000000000/data';
+                assert(assetURL===client.buildAssetURL('00000000-0000-0000-000000000000'), "buildAssetURL doesn't work")
+                done();
+            })
+            var dogEntity;
+            it('getEntity',function(done){
+                client.getEntity({type:'dog',name:'createEntityTestDog'}, function(err, response, dog){
+                    assert(!err, "createEntity returned an error")
+                    assert(dog, "createEntity returned a dog")
+                    assert(dog.get("uuid")!==null, "The dog's UUID was not returned")
+                    dogEntity=dog;
+                    done();
+                })
+            })
+            it('restoreEntity',function(done){
+                var serializedDog=dogEntity.serialize();
+                var dog=client.restoreEntity(serializedDog);
+                assert(dog, "restoreEntity did not return a dog")
+                assert(dog.get("uuid")===dogEntity.get("uuid"), "The dog's UUID was not the same as the serialized dog")
+                assert(dog.get("type")===dogEntity.get("type"), "The dog's type was not the same as the serialized dog")
+                assert(dog.get("name")===dogEntity.get("name"), "The dog's name was not the same as the serialized dog")
+
+                done();
+            })
+            var dogCollection;
+            it('createCollection',function(done){
+                client.createCollection({type:'dogs'},function(err, response, dogs){
+                    assert(!err, "createCollection returned an error");
+                    assert(dogs, "createCollection did not return a dogs collection");
+                    dogCollection=dogs;
+                    done();
+                })
+            })
+            it('restoreCollection',function(done){
+                var serializedDogs=dogCollection.serialize();
+                var dogs=client.restoreCollection(serializedDogs);
+                console.warn('restoreCollection',dogs, dogCollection);
+                assert(dogs._type===dogCollection._type, "The dog collection type was not the same as the serialized dog collection")
+                assert(dogs._qs==dogCollection._qs, "The query strings do not match")
+                assert(dogs._list.length===dogCollection._list.length, "The collections have a different number of entities")
+                done();
+            })
+            var activityUser;
+            before(function(done){
+                activityUser=new Usergrid.Entity({client:client,data:{"type":"user",'username':"testActivityUser"}});
+                console.warn(activityUser);
+                activityUser.fetch(function(err, data){
+                    console.warn(err, data, activityUser);
+                    if(err){
+                        activityUser.save(function(err, data){
+                            activityUser.set(data);
+                            done();
+                        });
+                    }else{
+                        activityUser.set(data);
+                        done();
+                    }
+                })
+            })
+            it('createUserActivity',function(done){
+                 var options = {
+                   "actor" : {
+                         "displayName" :"Test Activity User",
+                             "uuid" : activityUser.get("uuid"),
+                             "username" : "testActivityUser",
+                             "email" : "usergrid@apigee.com",
+                             "image" : {
+                                     "height" : 80,
+                                     "url" : "http://placekitten.com/80/80",
+                                     "width" : 80
+                             }
+                        },
+                        "verb" : "post",
+                       "content" : "test post for createUserActivity",
+                       "lat" : 48.856614,
+                       "lon" : 2.352222
+                     };
+                client.createUserActivity("testActivityUser", options, function(err, activity){
+                    assert(!err, "createUserActivity returned an error");
+                    assert(activity, "createUserActivity returned no activity object")
+                    done();
+                })
+            })
+            it('createUserActivityWithEntity',function(done){
+                    client.createUserActivityWithEntity(activityUser, "Another test activity with createUserActivityWithEntity", function(err, activity){
+                        assert(!err, "createUserActivityWithEntity returned an error "+err);
+                        assert(activity, "createUserActivityWithEntity returned no activity object")
+                        done();
+                    })
+            })
+            it('getFeedForUser',function(done){
+                client.getFeedForUser('testActivityUser', function(err, data, items){
+                    assert(!err, "getFeedForUser returned an error");
+                    assert(data, "getFeedForUser returned no data object")
+                    assert(items, "getFeedForUser returned no items array")
+                    done();
+                })
+            })
+            var testProperty="____test_item"+Math.floor(Math.random()*10000),
+                testPropertyValue="test"+Math.floor(Math.random()*10000),
+                testPropertyObjectValue={test:testPropertyValue};
+            it('set',function(done){
+                client.set(testProperty, testPropertyValue);
+                done();
+            })
+            it('get',function(done){
+                var retrievedValue=client.get(testProperty);
+                assert(retrievedValue===testPropertyValue, "Get is not working properly");
+                done();
+            })
+            it('setObject',function(done){
+                client.set(testProperty, testPropertyObjectValue);
+                done();
+            })
+            it('getObject',function(done){
+                var retrievedValue=client.get(testProperty);
+                assert(retrievedValue==testPropertyObjectValue, "getObject is not working properly");
+                done();
+            })
+            /*it('setToken',function(done){
+                client.setToken("dummytoken");
+                done();
+            })
+            it('getToken',function(done){
+                assert(client.getToken()==="dummytoken");
+                done();
+            })*/
+            it('remove property',function(done){
+                client.set(testProperty);
+                assert(client.get(testProperty)===null);
+                done();
+            })
+            var newUser;
+            it('signup',function(done){
+                client.signup("newUsergridUser", "Us3rgr1d15Aw3s0m3!", "usergrid@apigee.com", "Another Satisfied Developer", function(err, user){
+                    assert(!err, "signup returned an error");
+                    assert(user, "signup returned no user object")
+                    newUser=user;
+                    done();
+                })
+            })
+            it('login',function(done){
+                client.login("newUsergridUser", "Us3rgr1d15Aw3s0m3!", function(err, data, user){
+                    assert(!err, "login returned an error");
+                    assert(user, "login returned no user object")
+                    done();
+                })
+            })
+            /*it('reAuthenticateLite',function(done){
+                client.reAuthenticateLite(function(err){
+                    assert(!err, "reAuthenticateLite returned an error");
+                    done();
+                })
+            })*/
+            /*it('reAuthenticate',function(done){
+                client.reAuthenticate("usergrid@apigee.com", function(err, data, user, organizations, applications){
+                    assert(!err, "reAuthenticate returned an error");
+                    done();
+                })
+            })*/
+            /*it('loginFacebook',function(done){
+                assert(true, "Not sure how feasible it is to test this with Mocha")
+                done();
+            })*/
+            it('isLoggedIn',function(done){
+                assert(client.isLoggedIn()===true, "isLoggedIn is not detecting that we have logged in.")
+                done();
+            })
+            it('getLoggedInUser',function(done){
+                setTimeout(function(){
+                    client.getLoggedInUser(function(err, data, user){
+                        assert(!err, "getLoggedInUser returned an error");
+                        assert(user, "getLoggedInUser returned no user object")
+                        done();
+                    })
+                },1000);
+            })
+            before(function(done){
+                //please enjoy this musical interlude.
+                setTimeout(function(){done()},1000);
+            })
+            it('logout',function(done){
+                client.logout();
+                assert(null===client.getToken(), "we logged out, but the token still remains.")
+                done();
+            })
+            it('getLoggedInUser',function(done){
+                client.getLoggedInUser(function(err, data, user){
+                    assert(err, "getLoggedInUser should return an error after logout");
+                    assert(user, "getLoggedInUser should not return data after logout")
+                    done();
+                })
+            })
+            it('isLoggedIn',function(done){
+                assert(client.isLoggedIn()===false, "isLoggedIn still returns true after logout.")
+                done();
+            })
+            after(function (done) {
+                client.request({
+                    method: 'DELETE',
+                    endpoint: 'users/newUsergridUser'
+                }, function (err, data) {
+                    done();
+                });
+
+            })
+            it('buildCurlCall',function(done){
+                done();
+            })
+            it('getDisplayImage',function(done){
+                done();
+            })
+            after(function(done){
+                dogEntity.destroy();
+                //dogCollection.destroy();
+                //testGroup.destroy();
+                done();
+            })
+        });
+    });
+    describe('Usergrid Entity', function() {
+        var client = getClient();
+        var dog;
+        before(function(done) {
+            //Make sure our dog doesn't already exist
+            client.request({
+                method: 'DELETE',
+                endpoint: 'dogs/Rocky'
+            }, function(err, data) {
+                assert(true);
+                done();
+            });
+        });
+        it('should CREATE a new dog', function(done) {
+            var options = {
+                type: 'dogs',
+                name: 'Rocky'
+            }
+            dog=new Usergrid.Entity({client:client,data:options});
+            dog.save(function(err, entity) {
+                assert(!err, "dog not created");
+                done();
+            });
+        });
+        it('should RETRIEVE the dog', function(done) {
+            if (!dog) {
+                assert(false, "dog not created");
+                done();
+                return;
+            }
+            //once the dog is created, you can set single properties:
+            dog.fetch(function(err) {
+                assert(!err, "dog not fetched");
+                done();
+            });
+        });
+        it('should UPDATE the dog', function(done) {
+            if (!dog) {
+                assert(false, "dog not created");
+                done();
+                return;
+            }
+            //once the dog is created, you can set single properties:
+            dog.set('breed', 'Dinosaur');
+
+            //the set function can also take a JSON object:
+            var data = {
+                master: 'Fred',
+                state: 'hungry'
+            }
+            //set is additive, so previously set properties are not overwritten
+            dog.set(data);
+            //finally, call save on the object to save it back to the database
+            dog.save(function(err) {
+                assert(!err, "dog not saved");
+                done();
+            });
+        });
+        it('should DELETE the dog', function(done) {
+            if (!dog) {
+                assert(false, "dog not created");
+                done();
+                return;
+            }
+            //once the dog is created, you can set single properties:
+            dog.destroy(function(err) {
+                assert(!err, "dog not removed");
+                done();
+            });
+        });
+    });
+    describe('Usergrid Collections', function() {
+        var client = getClient();
+        var dog, dogs = {};
+
+        before(function(done) {
+            //Make sure our dog doesn't already exist
+            var options = {
+                type: 'dogs',
+                qs: {
+                    limit: 50
+                } //limit statement set to 50
+            }
+
+            client.createCollection(options, function(err, response, dogs) {
+                if (!err) {
+                    assert(!err, "could not retrieve list of dogs: " + dogs.error_description);
+                    //we got 50 dogs, now display the Entities:
+                    //do doggy cleanup
+                    //if (dogs.hasNextEntity()) {
+                    while (dogs.hasNextEntity()) {
+                        //get a reference to the dog
+                        var dog = dogs.getNextEntity();
+                        console.warn(dog);
+                        //notice('removing dog ' + dogname + ' from database');
+                        if(dog === null) continue;
+                        dog.destroy(function(err, data) {
+                            assert(!err, dog.get('name') + " not removed: " + data.error_description);
+                            if (!dogs.hasNextEntity()) {
+                                done();
+                            }
+                        });
+                    }
+                    //} else {
+                    done();
+                    //}
+                }
+            });
+        });
+        it('should CREATE a new dogs collection', function(done) {
+            var options = {
+                client:client,
+                type: 'dogs',
+                qs: {
+                    ql: 'order by index'
+                }
+            }
+            dogs=new Usergrid.Collection(options);
+            assert(dogs!==undefined&&dogs!==null, "could not create dogs collection");
+            done();
+        });
+        it('should CREATE dogs in the collection', function(done) {
+            this.timeout(10000);
+            var totalDogs = 30;
+            Array.apply(0, Array(totalDogs)).forEach(function(x, y) {
+                var dogNum = y + 1;
+                var options = {
+                    type: 'dogs',
+                    name: 'dog' + dogNum,
+                    index: y
+                }
+                dogs.addEntity(options, function(err, dog) {
+                    assert(!err, "dog not created");
+                    if (dogNum === totalDogs) {
+                        done();
+                    }
+                });
+            })
+        });
+        it('should RETRIEVE dogs from the collection', function(done) {
+            while (dogs.hasNextEntity()) {
+                //get a reference to the dog
+                dog = dogs.getNextEntity();
+            }
+            if (done) done();
+        });
+        it('should RETRIEVE the next page of dogs from the collection', function(done) {
+            if (dogs.hasNextPage()) {
+                dogs.getNextPage(function(err) {
+                    loop(done);
+                });
+            } else {
+                done();
+            }
+        });
+        it('should RETRIEVE the previous page of dogs from the collection', function(done) {
+            if (dogs.hasPreviousPage()) {
+                dogs.getPreviousPage(function(err) {
+                    loop(done);
+                });
+            } else {
+                done();
+            }
+        });
+        it('should RETRIEVE an entity by UUID.', function(done) {
+            var uuid=dogs.getFirstEntity().get("uuid")
+            dogs.getEntityByUUID(uuid,function(err, data){
+                assert(!err, "getEntityByUUID returned an error.");
+                assert(uuid==data.get("uuid"), "We didn't get the dog we asked for.");
+                done();
+            })
+        });
+        it('should RETRIEVE the first entity from the collection', function() {
+            assert(dogs.getFirstEntity(), "Could not retrieve the first dog");
+        });
+        it('should RETRIEVE the last entity from the collection', function() {
+            assert(dogs.getLastEntity(), "Could not retrieve the last dog");
+        });
+        it('should reset the paging', function() {
+            dogs.resetPaging();
+            assert(!dogs.hasPreviousPage(), "Could not resetPaging");
+        });
+        it('should reset the entity pointer', function() {
+            dogs.resetEntityPointer();
+            assert(!dogs.hasPrevEntity(), "Could not reset the pointer");
+            assert(dogs.hasNextEntity(), "Dog has no more entities");
+            assert(dogs.getNextEntity(), "Could not retrieve the next dog");
+        });
+        it('should RETRIEVE the next entity from the collection', function() {
+            assert(dogs.hasNextEntity(), "Dog has no more entities");
+            assert(dogs.getNextEntity(), "Could not retrieve the next dog");
+        });
+        it('should RETRIEVE the previous entity from the collection', function() {
+            assert(dogs.getNextEntity(), "Could not retrieve the next dog");
+            assert(dogs.hasPrevEntity(), "Dogs has no previous entities");
+            assert(dogs.getPrevEntity(), "Could not retrieve the previous dog");
+        });
+        it('should DELETE the entities from the collection', function(done) {
+            this.timeout(20000);
+            function remove(){
+                if(dogs.hasNextEntity()){
+                    dogs.destroyEntity(dogs.getNextEntity(),function(err, data){
+                        assert(!err, "Could not destroy dog.");
+                        remove();
+                    })
+                }else if(dogs.hasNextPage()){
+                    dogs.getNextPage();
+                    remove();
+                }else{
+                    done();
+                }
+            }
+            remove();
+        });
+    });
+    describe('Usergrid Counters', function() {
+        var client = getClient();
+        var counter;
+        var MINUTE = 1000 * 60;
+        var HOUR = MINUTE * 60;
+        var time = Date.now() - HOUR;
+
+        it('should CREATE a counter', function(done) {
+            counter = new Usergrid.Counter({
+                client: client,
+                data: {
+                    category: 'mocha_test',
+                    timestamp: time,
+                    name: "test",
+                    counters: {
+                        test: 0,
+                        test_counter: 0
+                    }
+                }
+            });
+            assert(counter, "Counter not created");
+            done();
+        });
+        it('should save a counter', function(done) {
+            counter.save(function(err, data) {
+                assert(!err, data.error_description);
+                done();
+            });
+        });
+        it('should reset a counter', function(done) {
+            time += MINUTE * 10
+            counter.set("timestamp", time);
+            counter.reset({
+                name: 'test'
+            }, function(err, data) {
+                assert(!err, data.error_description);
+                done();
+            });
+        });
+        it("should increment 'test' counter", function(done) {
+            time += MINUTE * 10
+            counter.set("timestamp", time);
+            counter.increment({
+                name: 'test',
+                value: 1
+            }, function(err, data) {
+                assert(!err, data.error_description);
+                done();
+            });
+        });
+        it("should increment 'test_counter' counter by 4", function(done) {
+            time += MINUTE * 10
+            counter.set("timestamp", time);
+            counter.increment({
+                name: 'test_counter',
+                value: 4
+            }, function(err, data) {
+                assert(!err, data.error_description);
+                done();
+            });
+        });
+        it("should decrement 'test' counter", function(done) {
+            time += MINUTE * 10
+            counter.set("timestamp", time);
+            counter.decrement({
+                name: 'test',
+                value: 1
+            }, function(err, data) {
+                assert(!err, data.error_description);
+                done();
+            });
+        });
+        it('should fetch the counter', function(done) {
+            counter.fetch(function(err, data) {
+                assert(!err, data.error_description);
+                done();
+            });
+        });
+        it('should fetch counter data', function(done) {
+            counter.getData({
+                resolution: 'all',
+                counters: ['test', 'test_counter']
+            }, function(err, data) {
+                assert(!err, data.error_description);
+                done();
+            });
+        });
+    });
+    describe('Usergrid Folders and Assets', function() {
+        var client = getClient();
+        var folder,
+            asset,
+            user,
+            image_type,
+            image_url = 'http://placekitten.com/160/90',
+        // image_url="https://api.usergrid.com/yourorgname/sandbox/assets/a4025e7a-8ab1-11e3-b56c-5d3c6e4ca93f/data",
+            test_image,
+            filesystem,
+            file_url,
+            filename = "kitten.jpg",
+            foldername = "kittens",
+            folderpath = '/test/' + Math.round(10000 * Math.random()),
+            filepath = [folderpath, foldername, filename].join('/');
+        before(function(done) {
+            var req = new XMLHttpRequest();
+            req.open('GET', image_url, true);
+            req.responseType = 'blob';
+            req.onload = function() {
+                test_image = req.response;
+                image_type = req.getResponseHeader('Content-Type');
+                done();
+            }
+            req.onerror = function(err) {
+                console.error(err);
+                done();
+            };
+            req.send(null);
+        });
+        before(function(done) {
+            this.timeout(10000);
+            client.request({
+                method: 'GET',
+                endpoint: 'Assets'
+            }, function(err, data) {
+                var assets = [];
+                if(data && data.entities && data.entities.length){
+                    assets.concat(data.entities.filter(function(asset) {
+                        return asset.name === filename
+                    }));
+                }
+                if (assets.length) {
+                    assets.forEach(function(asset) {
+                        client.request({
+                            method: 'DELETE',
+                            endpoint: 'assets/' + asset.uuid
+                        });
+                    });
+                    done();
+                } else {
+                    done();
+                }
+            });
+        });
+        before(function(done) {
+            this.timeout(10000);
+            client.request({
+                method: 'GET',
+                endpoint: 'folders'
+            }, function(err, data) {
+                var folders = [];
+                if(data && data.entities && data.entities.length){
+                    folders.concat(data.entities.filter(function(folder) {
+                        return folder.name === foldername
+                    }));
+                }
+                if (folders.length) {
+                    folders.forEach(function(folder) {
+                        client.request({
+                            method: 'DELETE',
+                            endpoint: 'folders/' + folder.uuid
+                        });
+                    });
+                    done();
+                } else {
+                    done();
+                }
+            });
+        });
+        before(function(done) {
+            this.timeout(10000);
+            user = new Usergrid.Entity({
+                client: client,
+                data: {
+                    type: 'users',
+                    username: 'assetuser'
+                }
+            });
+            user.fetch(function(err, data) {
+                if (err) {
+                    user.save(function() {
+                        done();
+                    })
+                } else {
+                    done();
+                }
+            })
+        });
+        it('should CREATE a folder', function(done) {
+            folder = new Usergrid.Folder({
+                client: client,
+                data: {
+                    name: foldername,
+                    owner: user.get("uuid"),
+                    path: folderpath
+                }
+            }, function(err, response, folder) {
+                assert(!err, err);
+                done();
+            });
+        });
+        it('should CREATE an asset', function(done) {
+            asset = new Usergrid.Asset({
+                client: client,
+                data: {
+                    name: filename,
+                    owner: user.get("uuid"),
+                    path: filepath
+                }
+            }, function(err, response, asset) {
+                if(err){
+                    assert(false, err);
+                }
+                done();
+            });
+        });
+        it('should RETRIEVE an asset', function(done) {
+            asset.fetch(function(err, response, entity){
+                if(err){
+                    assert(false, err);
+                }else{
+                    asset=entity;
+                }
+                done();
+            })
+        });
+        it('should upload asset data', function(done) {
+            this.timeout(5000);
+            asset.upload(test_image, function(err, response, asset) {
+                if(err){
+                    assert(false, err.error_description);
+                }
+                done();
+            });
+        });
+        it('should retrieve asset data', function(done) {
+            this.timeout(5000);
+            asset.download(function(err, response, asset) {
+                if(err){
+                    assert(false, err.error_description);
+                }
+                assert(asset.get('content-type') == test_image.type, "MIME types don't match");
+                assert(asset.get('size') == test_image.size, "sizes don't match");
+                done();
+            });
+        });
+        it('should add the asset to a folder', function(done) {
+            folder.addAsset({
+                asset: asset
+            }, function(err, data) {
+                if(err){
+                    assert(false, err.error_description);
+                }
+                done();
+            })
+        });
+        it('should list the assets from a folder', function(done) {
+            folder.getAssets(function(err, assets) {
+                if(err){
+                    assert(false, err.error_description);
+                }
+                done();
+            })
+        });
+        it('should remove the asset from a folder', function(done) {
+            folder.removeAsset({
+                asset: asset
+            }, function(err, data) {
+                if(err){
+                    assert(false, err.error_description);
+                }
+                done();
+            })
+        });
+        after(function(done) {
+            asset.destroy(function(err, data) {
+                if(err){
+                    assert(false, err.error_description);
+                }
+                done();
+            })
+        });
+        after(function(done) {
+            folder.destroy(function(err, data) {
+                if(err){
+                    assert(false, err.error_description);
+                }
+                done();
+            })
+        });
+    });
+});
diff --git a/tests/qunit/apigee_test.html b/tests/qunit/apigee_test.html
new file mode 100644
index 0000000..2dae960
--- /dev/null
+++ b/tests/qunit/apigee_test.html
@@ -0,0 +1,31 @@
+<!--
+  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>
+  <meta charset="utf-8">
+  <title>QUnit Example</title>
+  <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.12.0.css">
+</head>
+<body>
+  <div id="qunit"></div>
+  <div id="qunit-fixture"></div>
+  <script src="http://code.jquery.com/qunit/qunit-1.12.0.js"></script>
+  <script src="tests.js"></script>
+</body>
+</html>
diff --git a/tests/qunit/tests.js b/tests/qunit/tests.js
new file mode 100644
index 0000000..1d58b00
--- /dev/null
+++ b/tests/qunit/tests.js
@@ -0,0 +1,20 @@
+//
+// 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.
+//
+
+test( "hello test", function() {
+  ok( 1 == "1", "Passed!" );
+});
diff --git a/tests/resources/css/bootstrap-combined.min.css b/tests/resources/css/bootstrap-combined.min.css
new file mode 100755
index 0000000..e87551e
--- /dev/null
+++ b/tests/resources/css/bootstrap-combined.min.css
@@ -0,0 +1,18 @@
+/*!
+ * Bootstrap v2.0.4
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:20px}.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.127659574%;*margin-left:2.0744680846382977%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:99.99999998999999%;*width:99.94680850063828%}.row-fluid .span11{width:91.489361693%;*width:91.4361702036383%}.row-fluid .span10{width:82.97872339599999%;*width:82.92553190663828%}.row-fluid .span9{width:74.468085099%;*width:74.4148936096383%}.row-fluid .span8{width:65.95744680199999%;*width:65.90425531263828%}.row-fluid .span7{width:57.446808505%;*width:57.3936170156383%}.row-fluid .span6{width:48.93617020799999%;*width:48.88297871863829%}.row-fluid .span5{width:40.425531911%;*width:40.3723404216383%}.row-fluid .span4{width:31.914893614%;*width:31.8617021246383%}.row-fluid .span3{width:23.404255317%;*width:23.3510638276383%}.row-fluid .span2{width:14.89361702%;*width:14.8404255306383%}.row-fluid .span1{width:6.382978723%;*width:6.329787233638298%}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;content:""}.container-fluid:after{clear:both}p{margin:0 0 9px}p small{font-size:11px;color:#999}.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px}h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999}h1{font-size:30px;line-height:36px}h1 small{font-size:18px}h2{font-size:24px;line-height:36px}h2 small{font-size:18px}h3{font-size:18px;line-height:27px}h3 small{font-size:14px}h4,h5,h6{line-height:18px}h4{font-size:14px}h4 small{font-size:12px}h5{font-size:12px}h6{font-size:11px;color:#999;text-transform:uppercase}.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eee}.page-header h1{line-height:1}ul,ol{padding:0;margin:0 0 9px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}ul{list-style:disc}ol{list-style:decimal}li{line-height:18px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}dl{margin-bottom:18px}dt,dd{line-height:18px}dt{font-weight:bold;line-height:17px}dd{margin-left:9px}.dl-horizontal dt{float:left;width:120px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:130px}hr{margin:18px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}strong{font-weight:bold}em{font-style:italic}.muted{color:#999}abbr[title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px}blockquote small{display:block;line-height:18px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:18px;font-style:normal;line-height:18px}small{font-size:100%}cite{font-style:normal}code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:18px}pre code{padding:0;color:inherit;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 18px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:13.5px;color:#999}label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555}input,textarea{width:210px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-ms-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer}input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}.uneditable-textarea{width:auto;height:auto}select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px}select{width:220px;border:1px solid #bbb}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.radio,.checkbox{min-height:18px;padding-left:18px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:930px}input.span11,textarea.span11,.uneditable-input.span11{width:850px}input.span10,textarea.span10,.uneditable-input.span10{width:770px}input.span9,textarea.span9,.uneditable-input.span9{width:690px}input.span8,textarea.span8,.uneditable-input.span8{width:610px}input.span7,textarea.span7,.uneditable-input.span7{width:530px}input.span6,textarea.span6,.uneditable-input.span6{width:450px}input.span5,textarea.span5,.uneditable-input.span5{width:370px}input.span4,textarea.span4,.uneditable-input.span4{width:290px}input.span3,textarea.span3,.uneditable-input.span3{width:210px}input.span2,textarea.span2,.uneditable-input.span2{width:130px}input.span1,textarea.span1,.uneditable-input.span1{width:50px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee;border-color:#ddd}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853}.control-group.warning .checkbox:focus,.control-group.warning .radio:focus,.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48}.control-group.error .checkbox:focus,.control-group.error .radio:focus,.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847}.control-group.success .checkbox:focus,.control-group.success .radio:focus,.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;content:""}.form-actions:after{clear:both}.uneditable-input{overflow:hidden;white-space:nowrap;cursor:not-allowed;background-color:#fff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}:-moz-placeholder{color:#999}:-ms-input-placeholder{color:#999}::-webkit-input-placeholder{color:#999}.help-block,.help-inline{color:#555}.help-block{display:block;margin-bottom:9px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-prepend,.input-append{margin-bottom:5px}.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:middle;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{z-index:2}.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc}.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;height:18px;min-width:16px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #fff;vertical-align:middle;background-color:#eee;border:1px solid #ccc}.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append .uneditable-input{border-right-color:#ccc;border-left-color:#eee}.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:9px}legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:18px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:160px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:160px}.form-horizontal .help-block{margin-top:9px;margin-bottom:0}.form-horizontal .form-actions{padding-left:160px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:18px}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9}.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5}table .span1{float:none;width:44px;margin-left:0}table .span2{float:none;width:124px;margin-left:0}table .span3{float:none;width:204px;margin-left:0}table .span4{float:none;width:284px;margin-left:0}table .span5{float:none;width:364px;margin-left:0}table .span6{float:none;width:444px;margin-left:0}table .span7{float:none;width:524px;margin-left:0}table .span8{float:none;width:604px;margin-left:0}table .span9{float:none;width:684px;margin-left:0}table .span10{float:none;width:764px;margin-left:0}table .span11{float:none;width:844px;margin-left:0}table .span12{float:none;width:924px;margin-left:0}table .span13{float:none;width:1004px;margin-left:0}table .span14{float:none;width:1084px;margin-left:0}table .span15{float:none;width:1164px;margin-left:0}table .span16{float:none;width:1244px;margin-left:0}table .span17{float:none;width:1324px;margin-left:0}table .span18{float:none;width:1404px;margin-left:0}table .span19{float:none;width:1484px;margin-left:0}table .span20{float:none;width:1564px;margin-left:0}table .span21{float:none;width:1644px;margin-left:0}table .span22{float:none;width:1724px;margin-left:0}table .span23{float:none;width:1804px;margin-left:0}table .span24{float:none;width:1884px;margin-left:0}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0}.icon-white{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";opacity:.3;filter:alpha(opacity=30)}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown:hover .caret,.open .caret{opacity:1;filter:alpha(opacity=100)}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:4px 0;margin:1px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#fff;text-decoration:none;background-color:#08c}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:"\2191"}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0,0,0,0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-ms-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-ms-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 10px 4px;margin-bottom:0;*margin-left:.3em;font-size:13px;line-height:18px;*line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff',endColorstr='#e6e6e6',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-ms-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.btn-large [class^="icon-"]{margin-top:1px}.btn-small{padding:5px 9px;font-size:11px;line-height:16px}.btn-small [class^="icon-"]{margin-top:-1px}.btn-mini{padding:2px 6px;font-size:11px;line-height:14px}.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#ccc;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.btn-primary{background-color:#0074cc;*background-color:#05c;background-image:-ms-linear-gradient(top,#08c,#05c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#05c));background-image:-webkit-linear-gradient(top,#08c,#05c);background-image:-o-linear-gradient(top,#08c,#05c);background-image:-moz-linear-gradient(top,#08c,#05c);background-image:linear-gradient(top,#08c,#05c);background-repeat:repeat-x;border-color:#05c #05c #003580;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc',endColorstr='#0055cc',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:#05c;*background-color:#004ab3}.btn-primary:active,.btn-primary.active{background-color:#004099 \9}.btn-warning{background-color:#faa732;*background-color:#f89406;background-image:-ms-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450',endColorstr='#f89406',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{background-color:#da4f49;*background-color:#bd362f;background-image:-ms-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(top,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#bd362f',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{background-color:#5bb75b;*background-color:#51a351;background-image:-ms-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(top,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462',endColorstr='#51a351',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{background-color:#49afcd;*background-color:#2f96b4;background-image:-ms-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(top,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de',endColorstr='#2f96b4',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{background-color:#414141;*background-color:#222;background-image:-ms-linear-gradient(top,#555,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#555),to(#222));background-image:-webkit-linear-gradient(top,#555,#222);background-image:-o-linear-gradient(top,#555,#222);background-image:-moz-linear-gradient(top,#555,#222);background-image:linear-gradient(top,#555,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#555555',endColorstr='#222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-group{position:relative;*margin-left:.3em;*zoom:1}.btn-group:before,.btn-group:after{display:table;content:""}.btn-group:after{clear:both}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:9px;margin-bottom:9px}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1}.btn-group>.btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.dropdown-toggle{*padding-top:4px;padding-right:8px;*padding-bottom:4px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini.dropdown-toggle{padding-right:5px;padding-left:5px}.btn-group>.btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px}.btn-group>.btn-large.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#05c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:7px;margin-left:0}.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100)}.btn-mini .caret{margin-top:5px}.btn-small .caret{margin-top:6px}.btn-large .caret{margin-top:6px;border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-top:0;border-bottom:5px solid #000}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:.75;filter:alpha(opacity=75)}.alert{padding:8px 35px 8px 14px;margin-bottom:18px;color:#c09853;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert-heading{color:inherit}.alert .close{position:relative;top:-2px;right:-21px;line-height:18px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:18px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>.pull-right{float:right}.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px}.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333;border-bottom-color:#333}.nav>.dropdown.active>a:hover{color:#000;cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.navbar{*position:relative;*z-index:2;margin-bottom:18px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top,#333,#222);background-image:-ms-linear-gradient(top,#333,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#222));background-image:-webkit-linear-gradient(top,#333,#222);background-image:-o-linear-gradient(top,#333,#222);background-image:linear-gradient(top,#333,#222);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333',endColorstr='#222222',GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1)}.navbar .container{width:auto}.nav-collapse.collapse{height:auto}.navbar{color:#999}.navbar .brand:hover{text-decoration:none}.navbar .brand{display:block;float:left;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#999}.navbar .navbar-text{margin-bottom:0;line-height:40px}.navbar .navbar-link{color:#999}.navbar .navbar-link:hover{color:#fff}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn{margin:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#fff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none}.navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right}.navbar .nav>li{display:block;float:left}.navbar .nav>li>a{float:none;padding:9px 10px 11px;line-height:19px;color:#999;text-decoration:none;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar .btn{display:inline-block;padding:4px 10px 4px;margin:5px 5px 6px;line-height:18px}.navbar .btn-group{padding:5px 5px 6px;margin:0}.navbar .nav>li>a:hover{color:#fff;text-decoration:none;background-color:transparent}.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#fff;text-decoration:none;background-color:#222}.navbar .divider-vertical{width:1px;height:40px;margin:0 9px;overflow:hidden;background-color:#222;border-right:1px solid #333}.navbar .nav.pull-right{margin-right:0;margin-left:10px}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;background-color:#2c2c2c;*background-color:#222;background-image:-ms-linear-gradient(top,#333,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#222));background-image:-webkit-linear-gradient(top,#333,#222);background-image:-o-linear-gradient(top,#333,#222);background-image:linear-gradient(top,#333,#222);background-image:-moz-linear-gradient(top,#333,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333',endColorstr='#222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{background-color:#222;*background-color:#151515}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#080808 \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown .dropdown-toggle .caret,.navbar .nav li.dropdown.open .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar .nav li.dropdown.active .caret{opacity:1;filter:alpha(opacity=100)}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:transparent}.navbar .nav li.dropdown.active>.dropdown-toggle:hover{color:#fff}.navbar .pull-right .dropdown-menu,.navbar .dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right .dropdown-menu:before,.navbar .dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right .dropdown-menu:after,.navbar .dropdown-menu.pull-right:after{right:13px;left:auto}.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top,#fff,#f5f5f5);background-image:-ms-linear-gradient(top,#fff,#f5f5f5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#fff,#f5f5f5);background-image:-o-linear-gradient(top,#fff,#f5f5f5);background-image:linear-gradient(top,#fff,#f5f5f5);background-repeat:repeat-x;border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff',endColorstr='#f5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.breadcrumb li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb .divider{padding:0 5px;color:#999}.breadcrumb .active a{color:#333}.pagination{height:36px;margin:18px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination li{display:inline}.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0}.pagination a:hover,.pagination .active a{background-color:#f5f5f5}.pagination .active a{color:#999;cursor:default}.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999;cursor:default;background-color:transparent}.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pager{margin-bottom:18px;margin-left:0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;content:""}.pager:after{clear:both}.pager li{display:inline}.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next a{float:right}.pager .previous a{float:left}.pager .disabled a,.pager .disabled a:hover{color:#999;cursor:default;background-color:#fff}.modal-open .dropdown-menu{z-index:2050}.modal-open .dropdown.open{*z-index:2050}.modal-open .popover{z-index:2060}.modal-open .tooltip{z-index:2070}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:50%;left:50%;z-index:1050;width:560px;margin:-250px 0 0 -280px;overflow:auto;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-ms-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:50%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-body{max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.tooltip{position:absolute;z-index:1020;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-2px}.tooltip.right{margin-left:2px}.tooltip.bottom{margin-top:2px}.tooltip.left{margin-left:-2px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px}.popover.top{margin-top:-5px}.popover.right{margin-left:5px}.popover.bottom{margin-top:5px}.popover.left{margin-left:-5px}.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.popover .arrow{position:absolute;width:0;height:0}.popover-inner{width:280px;padding:3px;overflow:hidden;background:#000;background:rgba(0,0,0,0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3)}.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.popover-content{padding:14px;background-color:#fff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:18px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:0 1px 1px rgba(0,0,0,0.075);box-shadow:0 1px 1px rgba(0,0,0,0.075)}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px}.label,.badge{font-size:10.998px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:18px;margin-bottom:18px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-ms-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(top,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5',endColorstr='#f9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{width:0;height:18px;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(top,#149bdf,#0480be);background-image:-ms-linear-gradient(top,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#149bdf',endColorstr='#0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-ms-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .bar{background-color:#149bdf;background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-ms-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(top,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35',GradientType=0)}.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-ms-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(top,#62c462,#57a957);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462',endColorstr='#57a957',GradientType=0)}.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-ms-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(top,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de',endColorstr='#339bb9',GradientType=0)}.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-ms-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450',endColorstr='#f89406',GradientType=0)}.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:18px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:18px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel .item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-ms-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel .item>img{display:block;line-height:1}.carousel .active,.carousel .next,.carousel .prev{display:block}.carousel .active{left:0}.carousel .next,.carousel .prev{position:absolute;top:0;width:100%}.carousel .next{left:100%}.carousel .prev{left:-100%}.carousel .next.left,.carousel .prev.right{left:0}.carousel .active.left{left:-100%}.carousel .active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:10px 15px 5px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{color:#fff}.hero-unit{padding:60px;margin-bottom:30px;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}
+/*!
+ * Bootstrap Responsive v2.0.4
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}@media(max-width:767px){.visible-phone{display:inherit!important}.hidden-phone{display:none!important}.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}}@media(min-width:768px) and (max-width:979px){.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:18px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.modal{position:absolute;top:10px;right:10px;left:10px;width:auto;margin:0}.modal.fade.in{top:auto}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:auto;margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:20px}.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.762430939%;*margin-left:2.709239449638298%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:99.999999993%;*width:99.9468085036383%}.row-fluid .span11{width:91.436464082%;*width:91.38327259263829%}.row-fluid .span10{width:82.87292817100001%;*width:82.8197366816383%}.row-fluid .span9{width:74.30939226%;*width:74.25620077063829%}.row-fluid .span8{width:65.74585634900001%;*width:65.6926648596383%}.row-fluid .span7{width:57.182320438000005%;*width:57.129128948638304%}.row-fluid .span6{width:48.618784527%;*width:48.5655930376383%}.row-fluid .span5{width:40.055248616%;*width:40.0020571266383%}.row-fluid .span4{width:31.491712705%;*width:31.4385212156383%}.row-fluid .span3{width:22.928176794%;*width:22.874985304638297%}.row-fluid .span2{width:14.364640883%;*width:14.311449393638298%}.row-fluid .span1{width:5.801104972%;*width:5.747913482638298%}input,textarea,.uneditable-input{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:714px}input.span11,textarea.span11,.uneditable-input.span11{width:652px}input.span10,textarea.span10,.uneditable-input.span10{width:590px}input.span9,textarea.span9,.uneditable-input.span9{width:528px}input.span8,textarea.span8,.uneditable-input.span8{width:466px}input.span7,textarea.span7,.uneditable-input.span7{width:404px}input.span6,textarea.span6,.uneditable-input.span6{width:342px}input.span5,textarea.span5,.uneditable-input.span5{width:280px}input.span4,textarea.span4,.uneditable-input.span4{width:218px}input.span3,textarea.span3,.uneditable-input.span3{width:156px}input.span2,textarea.span2,.uneditable-input.span2{width:94px}input.span1,textarea.span1,.uneditable-input.span1{width:32px}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;content:""}.row:after{clear:both}[class*="span"]{float:left;margin-left:30px}.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:28px;margin-left:2.564102564%;*margin-left:2.510911074638298%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145300001%;*width:91.3997999636383%}.row-fluid .span10{width:82.905982906%;*width:82.8527914166383%}.row-fluid .span9{width:74.358974359%;*width:74.30578286963829%}.row-fluid .span8{width:65.81196581200001%;*width:65.7587743226383%}.row-fluid .span7{width:57.264957265%;*width:57.2117657756383%}.row-fluid .span6{width:48.717948718%;*width:48.6647572286383%}.row-fluid .span5{width:40.170940171000005%;*width:40.117748681638304%}.row-fluid .span4{width:31.623931624%;*width:31.5707401346383%}.row-fluid .span3{width:23.076923077%;*width:23.0237315876383%}.row-fluid .span2{width:14.529914530000001%;*width:14.4767230406383%}.row-fluid .span1{width:5.982905983%;*width:5.929714493638298%}input,textarea,.uneditable-input{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:1160px}input.span11,textarea.span11,.uneditable-input.span11{width:1060px}input.span10,textarea.span10,.uneditable-input.span10{width:960px}input.span9,textarea.span9,.uneditable-input.span9{width:860px}input.span8,textarea.span8,.uneditable-input.span8{width:760px}input.span7,textarea.span7,.uneditable-input.span7{width:660px}input.span6,textarea.span6,.uneditable-input.span6{width:560px}input.span5,textarea.span5,.uneditable-input.span5{width:460px}input.span4,textarea.span4,.uneditable-input.span4{width:360px}input.span3,textarea.span3,.uneditable-input.span3{width:260px}input.span2,textarea.span2,.uneditable-input.span2{width:160px}input.span1,textarea.span1,.uneditable-input.span1{width:60px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:18px}.navbar-fixed-bottom{margin-top:18px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 9px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#999;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#222}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:block;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222;border-bottom:1px solid #222;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
\ No newline at end of file
diff --git a/tests/resources/css/mocha.css b/tests/resources/css/mocha.css
new file mode 100644
index 0000000..42b9798
--- /dev/null
+++ b/tests/resources/css/mocha.css
@@ -0,0 +1,270 @@
+@charset "utf-8";
+
+body {
+  margin:0;
+}
+
+#mocha {
+  font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
+  margin: 60px 50px;
+}
+
+#mocha ul,
+#mocha li {
+  margin: 0;
+  padding: 0;
+}
+
+#mocha ul {
+  list-style: none;
+}
+
+#mocha h1,
+#mocha h2 {
+  margin: 0;
+}
+
+#mocha h1 {
+  margin-top: 15px;
+  font-size: 1em;
+  font-weight: 200;
+}
+
+#mocha h1 a {
+  text-decoration: none;
+  color: inherit;
+}
+
+#mocha h1 a:hover {
+  text-decoration: underline;
+}
+
+#mocha .suite .suite h1 {
+  margin-top: 0;
+  font-size: .8em;
+}
+
+#mocha .hidden {
+  display: none;
+}
+
+#mocha h2 {
+  font-size: 12px;
+  font-weight: normal;
+  cursor: pointer;
+}
+
+#mocha .suite {
+  margin-left: 15px;
+}
+
+#mocha .test {
+  margin-left: 15px;
+  overflow: hidden;
+}
+
+#mocha .test.pending:hover h2::after {
+  content: '(pending)';
+  font-family: arial, sans-serif;
+}
+
+#mocha .test.pass.medium .duration {
+  background: #c09853;
+}
+
+#mocha .test.pass.slow .duration {
+  background: #b94a48;
+}
+
+#mocha .test.pass::before {
+  content: '✓';
+  font-size: 12px;
+  display: block;
+  float: left;
+  margin-right: 5px;
+  color: #00d6b2;
+}
+
+#mocha .test.pass .duration {
+  font-size: 9px;
+  margin-left: 5px;
+  padding: 2px 5px;
+  color: #fff;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
+  -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
+  box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
+  -webkit-border-radius: 5px;
+  -moz-border-radius: 5px;
+  -ms-border-radius: 5px;
+  -o-border-radius: 5px;
+  border-radius: 5px;
+}
+
+#mocha .test.pass.fast .duration {
+  display: none;
+}
+
+#mocha .test.pending {
+  color: #0b97c4;
+}
+
+#mocha .test.pending::before {
+  content: 'â—¦';
+  color: #0b97c4;
+}
+
+#mocha .test.fail {
+  color: #c00;
+}
+
+#mocha .test.fail pre {
+  color: black;
+}
+
+#mocha .test.fail::before {
+  content: '✖';
+  font-size: 12px;
+  display: block;
+  float: left;
+  margin-right: 5px;
+  color: #c00;
+}
+
+#mocha .test pre.error {
+  color: #c00;
+  max-height: 300px;
+  overflow: auto;
+}
+
+/**
+ * (1): approximate for browsers not supporting calc
+ * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border)
+ *      ^^ seriously
+ */
+#mocha .test pre {
+  display: block;
+  float: left;
+  clear: left;
+  font: 12px/1.5 monaco, monospace;
+  margin: 5px;
+  padding: 15px;
+  border: 1px solid #eee;
+  max-width: 85%; /*(1)*/
+  max-width: calc(100% - 42px); /*(2)*/
+  word-wrap: break-word;
+  border-bottom-color: #ddd;
+  -webkit-border-radius: 3px;
+  -webkit-box-shadow: 0 1px 3px #eee;
+  -moz-border-radius: 3px;
+  -moz-box-shadow: 0 1px 3px #eee;
+  border-radius: 3px;
+}
+
+#mocha .test h2 {
+  position: relative;
+}
+
+#mocha .test a.replay {
+  position: absolute;
+  top: 3px;
+  right: 0;
+  text-decoration: none;
+  vertical-align: middle;
+  display: block;
+  width: 15px;
+  height: 15px;
+  line-height: 15px;
+  text-align: center;
+  background: #eee;
+  font-size: 15px;
+  -moz-border-radius: 15px;
+  border-radius: 15px;
+  -webkit-transition: opacity 200ms;
+  -moz-transition: opacity 200ms;
+  transition: opacity 200ms;
+  opacity: 0.3;
+  color: #888;
+}
+
+#mocha .test:hover a.replay {
+  opacity: 1;
+}
+
+#mocha-report.pass .test.fail {
+  display: none;
+}
+
+#mocha-report.fail .test.pass {
+  display: none;
+}
+
+#mocha-report.pending .test.pass,
+#mocha-report.pending .test.fail {
+  display: none;
+}
+#mocha-report.pending .test.pass.pending {
+  display: block;
+}
+
+#mocha-error {
+  color: #c00;
+  font-size: 1.5em;
+  font-weight: 100;
+  letter-spacing: 1px;
+}
+
+#mocha-stats {
+  position: fixed;
+  top: 15px;
+  right: 10px;
+  font-size: 12px;
+  margin: 0;
+  color: #888;
+  z-index: 1;
+}
+
+#mocha-stats .progress {
+  float: right;
+  padding-top: 0;
+}
+
+#mocha-stats em {
+  color: black;
+}
+
+#mocha-stats a {
+  text-decoration: none;
+  color: inherit;
+}
+
+#mocha-stats a:hover {
+  border-bottom: 1px solid #eee;
+}
+
+#mocha-stats li {
+  display: inline-block;
+  margin: 0 5px;
+  list-style: none;
+  padding-top: 11px;
+}
+
+#mocha-stats canvas {
+  width: 40px;
+  height: 40px;
+}
+
+#mocha code .comment { color: #ddd; }
+#mocha code .init { color: #2f6fad; }
+#mocha code .string { color: #5890ad; }
+#mocha code .keyword { color: #8a6343; }
+#mocha code .number { color: #2f6fad; }
+
+@media screen and (max-device-width: 480px) {
+  #mocha {
+    margin: 60px 0px;
+  }
+
+  #mocha #stats {
+    position: absolute;
+  }
+}
diff --git a/tests/resources/css/styles.css b/tests/resources/css/styles.css
new file mode 100755
index 0000000..7492f93
--- /dev/null
+++ b/tests/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
diff --git a/tests/resources/images/apigee.png b/tests/resources/images/apigee.png
new file mode 100755
index 0000000..c0d0f84
--- /dev/null
+++ b/tests/resources/images/apigee.png
Binary files differ
diff --git a/tests/resources/js/blanket_mocha.min.js b/tests/resources/js/blanket_mocha.min.js
new file mode 100644
index 0000000..db01cab
--- /dev/null
+++ b/tests/resources/js/blanket_mocha.min.js
@@ -0,0 +1 @@
+(function(e){(function(t,n){"use strict";typeof e=="function"&&e.amd?e(["exports"],n):typeof exports!="undefined"?n(exports):n(t.esprima={})})(this,function(e){"use strict";function m(e,t){if(!e)throw new Error("ASSERT: "+t)}function g(e,t){return u.slice(e,t)}function y(e){return"0123456789".indexOf(e)>=0}function b(e){return"0123456789abcdefABCDEF".indexOf(e)>=0}function w(e){return"01234567".indexOf(e)>=0}function E(e){return e===" "||e===" "||e==="\f"||e==="\u00a0"||e.charCodeAt(0)>=5760&&"\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\ufeff".indexOf(e)>=0}function S(e){return e==="\n"||e==="\r"||e==="\u2028"||e==="\u2029"}function x(e){return e==="$"||e==="_"||e==="\\"||e>="a"&&e<="z"||e>="A"&&e<="Z"||e.charCodeAt(0)>=128&&o.NonAsciiIdentifierStart.test(e)}function T(e){return e==="$"||e==="_"||e==="\\"||e>="a"&&e<="z"||e>="A"&&e<="Z"||e>="0"&&e<="9"||e.charCodeAt(0)>=128&&o.NonAsciiIdentifierPart.test(e)}function N(e){switch(e){case"class":case"enum":case"export":case"extends":case"import":case"super":return!0}return!1}function C(e){switch(e){case"implements":case"interface":case"package":case"private":case"protected":case"public":case"static":case"yield":case"let":return!0}return!1}function k(e){return e==="eval"||e==="arguments"}function L(e){var t=!1;switch(e.length){case 2:t=e==="if"||e==="in"||e==="do";break;case 3:t=e==="var"||e==="for"||e==="new"||e==="try";break;case 4:t=e==="this"||e==="else"||e==="case"||e==="void"||e==="with";break;case 5:t=e==="while"||e==="break"||e==="catch"||e==="throw";break;case 6:t=e==="return"||e==="typeof"||e==="delete"||e==="switch";break;case 7:t=e==="default"||e==="finally";break;case 8:t=e==="function"||e==="continue"||e==="debugger";break;case 10:t=e==="instanceof"}if(t)return!0;switch(e){case"const":return!0;case"yield":case"let":return!0}return a&&C(e)?!0:N(e)}function A(){var e,t,n;t=!1,n=!1;while(f<h){e=u[f];if(n)e=u[f++],S(e)&&(n=!1,e==="\r"&&u[f]==="\n"&&++f,++l,c=f);else if(t)S(e)?(e==="\r"&&u[f+1]==="\n"&&++f,++l,++f,c=f,f>=h&&R({},s.UnexpectedToken,"ILLEGAL")):(e=u[f++],f>=h&&R({},s.UnexpectedToken,"ILLEGAL"),e==="*"&&(e=u[f],e==="/"&&(++f,t=!1)));else if(e==="/"){e=u[f+1];if(e==="/")f+=2,n=!0;else{if(e!=="*")break;f+=2,t=!0,f>=h&&R({},s.UnexpectedToken,"ILLEGAL")}}else if(E(e))++f;else{if(!S(e))break;++f,e==="\r"&&u[f]==="\n"&&++f,++l,c=f}}}function O(e){var t,n,r,i=0;n=e==="u"?4:2;for(t=0;t<n;++t){if(!(f<h&&b(u[f])))return"";r=u[f++],i=i*16+"0123456789abcdef".indexOf(r.toLowerCase())}return String.fromCharCode(i)}function M(){var e,n,r,i;e=u[f];if(!x(e))return;n=f;if(e==="\\"){++f;if(u[f]!=="u")return;++f,i=f,e=O("u");if(e){if(e==="\\"||!x(e))return;r=e}else f=i,r="u"}else r=u[f++];while(f<h){e=u[f];if(!T(e))break;if(e==="\\"){++f;if(u[f]!=="u")return;++f,i=f,e=O("u");if(e){if(e==="\\"||!T(e))return;r+=e}else f=i,r+="u"}else r+=u[f++]}return r.length===1?{type:t.Identifier,value:r,lineNumber:l,lineStart:c,range:[n,f]}:L(r)?{type:t.Keyword,value:r,lineNumber:l,lineStart:c,range:[n,f]}:r==="null"?{type:t.NullLiteral,value:r,lineNumber:l,lineStart:c,range:[n,f]}:r==="true"||r==="false"?{type:t.BooleanLiteral,value:r,lineNumber:l,lineStart:c,range:[n,f]}:{type:t.Identifier,value:r,lineNumber:l,lineStart:c,range:[n,f]}}function _(){var e=f,n=u[f],r,i,s;if(n===";"||n==="{"||n==="}")return++f,{type:t.Punctuator,value:n,lineNumber:l,lineStart:c,range:[e,f]};if(n===","||n==="("||n===")")return++f,{type:t.Punctuator,value:n,lineNumber:l,lineStart:c,range:[e,f]};r=u[f+1];if(n==="."&&!y(r))return{type:t.Punctuator,value:u[f++],lineNumber:l,lineStart:c,range:[e,f]};i=u[f+2],s=u[f+3];if(n===">"&&r===">"&&i===">"&&s==="=")return f+=4,{type:t.Punctuator,value:">>>=",lineNumber:l,lineStart:c,range:[e,f]};if(n==="="&&r==="="&&i==="=")return f+=3,{type:t.Punctuator,value:"===",lineNumber:l,lineStart:c,range:[e,f]};if(n==="!"&&r==="="&&i==="=")return f+=3,{type:t.Punctuator,value:"!==",lineNumber:l,lineStart:c,range:[e,f]};if(n===">"&&r===">"&&i===">")return f+=3,{type:t.Punctuator,value:">>>",lineNumber:l,lineStart:c,range:[e,f]};if(n==="<"&&r==="<"&&i==="=")return f+=3,{type:t.Punctuator,value:"<<=",lineNumber:l,lineStart:c,range:[e,f]};if(n===">"&&r===">"&&i==="=")return f+=3,{type:t.Punctuator,value:">>=",lineNumber:l,lineStart:c,range:[e,f]};if(r==="="&&"<>=!+-*%&|^/".indexOf(n)>=0)return f+=2,{type:t.Punctuator,value:n+r,lineNumber:l,lineStart:c,range:[e,f]};if(n===r&&"+-<>&|".indexOf(n)>=0&&"+-<>&|".indexOf(r)>=0)return f+=2,{type:t.Punctuator,value:n+r,lineNumber:l,lineStart:c,range:[e,f]};if("[]<>+-*%&|^!~?:=/".indexOf(n)>=0)return{type:t.Punctuator,value:u[f++],lineNumber:l,lineStart:c,range:[e,f]}}function D(){var e,n,r;r=u[f],m(y(r)||r===".","Numeric literal must start with a decimal digit or a decimal point"),n=f,e="";if(r!=="."){e=u[f++],r=u[f];if(e==="0"){if(r==="x"||r==="X"){e+=u[f++];while(f<h){r=u[f];if(!b(r))break;e+=u[f++]}return e.length<=2&&R({},s.UnexpectedToken,"ILLEGAL"),f<h&&(r=u[f],x(r)&&R({},s.UnexpectedToken,"ILLEGAL")),{type:t.NumericLiteral,value:parseInt(e,16),lineNumber:l,lineStart:c,range:[n,f]}}if(w(r)){e+=u[f++];while(f<h){r=u[f];if(!w(r))break;e+=u[f++]}return f<h&&(r=u[f],(x(r)||y(r))&&R({},s.UnexpectedToken,"ILLEGAL")),{type:t.NumericLiteral,value:parseInt(e,8),octal:!0,lineNumber:l,lineStart:c,range:[n,f]}}y(r)&&R({},s.UnexpectedToken,"ILLEGAL")}while(f<h){r=u[f];if(!y(r))break;e+=u[f++]}}if(r==="."){e+=u[f++];while(f<h){r=u[f];if(!y(r))break;e+=u[f++]}}if(r==="e"||r==="E"){e+=u[f++],r=u[f];if(r==="+"||r==="-")e+=u[f++];r=u[f];if(y(r)){e+=u[f++];while(f<h){r=u[f];if(!y(r))break;e+=u[f++]}}else r="character "+r,f>=h&&(r="<end>"),R({},s.UnexpectedToken,"ILLEGAL")}return f<h&&(r=u[f],x(r)&&R({},s.UnexpectedToken,"ILLEGAL")),{type:t.NumericLiteral,value:parseFloat(e),lineNumber:l,lineStart:c,range:[n,f]}}function P(){var e="",n,r,i,o,a,p,d=!1;n=u[f],m(n==="'"||n==='"',"String literal must starts with a quote"),r=f,++f;while(f<h){i=u[f++];if(i===n){n="";break}if(i==="\\"){i=u[f++];if(!S(i))switch(i){case"n":e+="\n";break;case"r":e+="\r";break;case"t":e+="  ";break;case"u":case"x":p=f,a=O(i),a?e+=a:(f=p,e+=i);break;case"b":e+="\b";break;case"f":e+="\f";break;case"v":e+="";break;default:w(i)?(o="01234567".indexOf(i),o!==0&&(d=!0),f<h&&w(u[f])&&(d=!0,o=o*8+"01234567".indexOf(u[f++]),"0123".indexOf(i)>=0&&f<h&&w(u[f])&&(o=o*8+"01234567".indexOf(u[f++]))),e+=String.fromCharCode(o)):e+=i}else++l,i==="\r"&&u[f]==="\n"&&++f}else{if(S(i))break;e+=i}}return n!==""&&R({},s.UnexpectedToken,"ILLEGAL"),{type:t.StringLiteral,value:e,octal:d,lineNumber:l,lineStart:c,range:[r,f]}}function H(){var e,t,n,r,i,o,a=!1,l,c=!1;p=null,A(),n=f,t=u[f],m(t==="/","Regular expression literal must start with a slash"),e=u[f++];while(f<h){t=u[f++],e+=t;if(t==="\\")t=u[f++],S(t)&&R({},s.UnterminatedRegExp),e+=t;else if(a)t==="]"&&(a=!1);else{if(t==="/"){c=!0;break}t==="["?a=!0:S(t)&&R({},s.UnterminatedRegExp)}}c||R({},s.UnterminatedRegExp),r=e.substr(1,e.length-2),i="";while(f<h){t=u[f];if(!T(t))break;++f;if(t==="\\"&&f<h){t=u[f];if(t==="u"){++f,l=f,t=O("u");if(t){i+=t,e+="\\u";for(;l<f;++l)e+=u[l]}else f=l,i+="u",e+="\\u"}else e+="\\"}else i+=t,e+=t}try{o=new RegExp(r,i)}catch(d){R({},s.InvalidRegExp)}return{literal:e,value:o,range:[n,f]}}function B(e){return e.type===t.Identifier||e.type===t.Keyword||e.type===t.BooleanLiteral||e.type===t.NullLiteral}function j(){var e,n;A();if(f>=h)return{type:t.EOF,lineNumber:l,lineStart:c,range:[f,f]};n=_();if(typeof n!="undefined")return n;e=u[f];if(e==="'"||e==='"')return P();if(e==="."||y(e))return D();n=M();if(typeof n!="undefined")return n;R({},s.UnexpectedToken,"ILLEGAL")}function F(){var e;return p?(f=p.range[1],l=p.lineNumber,c=p.lineStart,e=p,p=null,e):(p=null,j())}function I(){var e,t,n;return p!==null?p:(e=f,t=l,n=c,p=j(),f=e,l=t,c=n,p)}function q(){var e,t,n,r;return e=f,t=l,n=c,A(),r=l!==t,f=e,l=t,c=n,r}function R(e,t){var n,r=Array.prototype.slice.call(arguments,2),i=t.replace(/%(\d)/g,function(e,t){return r[t]||""});throw typeof e.lineNumber=="number"?(n=new Error("Line "+e.lineNumber+": "+i),n.index=e.range[0],n.lineNumber=e.lineNumber,n.column=e.range[0]-c+1):(n=new Error("Line "+l+": "+i),n.index=f,n.lineNumber=l,n.column=f-c+1),n}function U(){try{R.apply(null,arguments)}catch(e){if(!v.errors)throw e;v.errors.push(e)}}function z(e){e.type===t.EOF&&R(e,s.UnexpectedEOS),e.type===t.NumericLiteral&&R(e,s.UnexpectedNumber),e.type===t.StringLiteral&&R(e,s.UnexpectedString),e.type===t.Identifier&&R(e,s.UnexpectedIdentifier);if(e.type===t.Keyword){if(N(e.value))R(e,s.UnexpectedReserved);else if(a&&C(e.value)){U(e,s.StrictReservedWord);return}R(e,s.UnexpectedToken,e.value)}R(e,s.UnexpectedToken,e.value)}function W(e){var n=F();(n.type!==t.Punctuator||n.value!==e)&&z(n)}function X(e){var n=F();(n.type!==t.Keyword||n.value!==e)&&z(n)}function V(e){var n=I();return n.type===t.Punctuator&&n.value===e}function $(e){var n=I();return n.type===t.Keyword&&n.value===e}function J(){var e=I(),n=e.value;return e.type!==t.Punctuator?!1:n==="="||n==="*="||n==="/="||n==="%="||n==="+="||n==="-="||n==="<<="||n===">>="||n===">>>="||n==="&="||n==="^="||n==="|="}function K(){var e,n;if(u[f]===";"){F();return}n=l,A();if(l!==n)return;if(V(";")){F();return}e=I(),e.type!==t.EOF&&!V("}")&&z(e)}function Q(e){return e.type===r.Identifier||e.type===r.MemberExpression}function G(){var e=[];W("[");while(!V("]"))V(",")?(F(),e.push(null)):(e.push(Tt()),V("]")||W(","));return W("]"),{type:r.ArrayExpression,elements:e}}function Y(e,t){var n,i;return n=a,i=Gt(),t&&a&&k(e[0].name)&&U(t,s.StrictParamName),a=n,{type:r.FunctionExpression,id:null,params:e,defaults:[],body:i,rest:null,generator:!1,expression:!1}}function Z(){var e=F();return e.type===t.StringLiteral||e.type===t.NumericLiteral?(a&&e.octal&&U(e,s.StrictOctalLiteral),ln(e)):{type:r.Identifier,name:e.value}}function et(){var e,n,i,o;e=I();if(e.type===t.Identifier)return i=Z(),e.value==="get"&&!V(":")?(n=Z(),W("("),W(")"),{type:r.Property,key:n,value:Y([]),kind:"get"}):e.value==="set"&&!V(":")?(n=Z(),W("("),e=I(),e.type!==t.Identifier?(W(")"),U(e,s.UnexpectedToken,e.value),{type:r.Property,key:n,value:Y([]),kind:"set"}):(o=[Lt()],W(")"),{type:r.Property,key:n,value:Y(o,e),kind:"set"})):(W(":"),{type:r.Property,key:i,value:Tt(),kind:"init"});if(e.type!==t.EOF&&e.type!==t.Punctuator)return n=Z(),W(":"),{type:r.Property,key:n,value:Tt(),kind:"init"};z(e)}function tt(){var e=[],t,n,o,u={},f=String;W("{");while(!V("}"))t=et(),t.key.type===r.Identifier?n=t.key.name:n=f(t.key.value),o=t.kind==="init"?i.Data:t.kind==="get"?i.Get:i.Set,Object.prototype.hasOwnProperty.call(u,n)?(u[n]===i.Data?a&&o===i.Data?U({},s.StrictDuplicateProperty):o!==i.Data&&U({},s.AccessorDataProperty):o===i.Data?U({},s.AccessorDataProperty):u[n]&o&&U({},s.AccessorGetSet),u[n]|=o):u[n]=o,e.push(t),V("}")||W(",");return W("}"),{type:r.ObjectExpression,properties:e}}function nt(){var e;return W("("),e=Nt(),W(")"),e}function rt(){var e=I(),n=e.type;if(n===t.Identifier)return{type:r.Identifier,name:F().value};if(n===t.StringLiteral||n===t.NumericLiteral)return a&&e.octal&&U(e,s.StrictOctalLiteral),ln(F());if(n===t.Keyword){if($("this"))return F(),{type:r.ThisExpression};if($("function"))return Zt()}return n===t.BooleanLiteral?(F(),e.value=e.value==="true",ln(e)):n===t.NullLiteral?(F(),e.value=null,ln(e)):V("[")?G():V("{")?tt():V("(")?nt():V("/")||V("/=")?ln(H()):z(F())}function it(){var e=[];W("(");if(!V(")"))while(f<h){e.push(Tt());if(V(")"))break;W(",")}return W(")"),e}function st(){var e=F();return B(e)||z(e),{type:r.Identifier,name:e.value}}function ot(){return W("."),st()}function ut(){var e;return W("["),e=Nt(),W("]"),e}function at(){var e;return X("new"),e={type:r.NewExpression,callee:lt(),arguments:[]},V("(")&&(e.arguments=it()),e}function ft(){var e;e=$("new")?at():rt();while(V(".")||V("[")||V("("))V("(")?e={type:r.CallExpression,callee:e,arguments:it()}:V("[")?e={type:r.MemberExpression,computed:!0,object:e,property:ut()}:e={type:r.MemberExpression,computed:!1,object:e,property:ot()};return e}function lt(){var e;e=$("new")?at():rt();while(V(".")||V("["))V("[")?e={type:r.MemberExpression,computed:!0,object:e,property:ut()}:e={type:r.MemberExpression,computed:!1,object:e,property:ot()};return e}function ct(){var e=ft(),n;return n=I(),n.type!==t.Punctuator?e:((V("++")||V("--"))&&!q()&&(a&&e.type===r.Identifier&&k(e.name)&&U({},s.StrictLHSPostfix),Q(e)||U({},s.InvalidLHSInAssignment),e={type:r.UpdateExpression,operator:F().value,argument:e,prefix:!1}),e)}function ht(){var e,n;return e=I(),e.type!==t.Punctuator&&e.type!==t.Keyword?ct():V("++")||V("--")?(e=F(),n=ht(),a&&n.type===r.Identifier&&k(n.name)&&U({},s.StrictLHSPrefix),Q(n)||U({},s.InvalidLHSInAssignment),n={type:r.UpdateExpression,operator:e.value,argument:n,prefix:!0},n):V("+")||V("-")||V("~")||V("!")?(n={type:r.UnaryExpression,operator:F().value,argument:ht(),prefix:!0},n):$("delete")||$("void")||$("typeof")?(n={type:r.UnaryExpression,operator:F().value,argument:ht(),prefix:!0},a&&n.operator==="delete"&&n.argument.type===r.Identifier&&U({},s.StrictDelete),n):ct()}function pt(){var e=ht();while(V("*")||V("/")||V("%"))e={type:r.BinaryExpression,operator:F().value,left:e,right:ht()};return e}function dt(){var e=pt();while(V("+")||V("-"))e={type:r.BinaryExpression,operator:F().value,left:e,right:pt()};return e}function vt(){var e=dt();while(V("<<")||V(">>")||V(">>>"))e={type:r.BinaryExpression,operator:F().value,left:e,right:dt()};return e}function mt(){var e,t;t=d.allowIn,d.allowIn=!0,e=vt();while(V("<")||V(">")||V("<=")||V(">=")||t&&$("in")||$("instanceof"))e={type:r.BinaryExpression,operator:F().value,left:e,right:vt()};return d.allowIn=t,e}function gt(){var e=mt();while(V("==")||V("!=")||V("===")||V("!=="))e={type:r.BinaryExpression,operator:F().value,left:e,right:mt()};return e}function yt(){var e=gt();while(V("&"))F(),e={type:r.BinaryExpression,operator:"&",left:e,right:gt()};return e}function bt(){var e=yt();while(V("^"))F(),e={type:r.BinaryExpression,operator:"^",left:e,right:yt()};return e}function wt(){var e=bt();while(V("|"))F(),e={type:r.BinaryExpression,operator:"|",left:e,right:bt()};return e}function Et(){var e=wt();while(V("&&"))F(),e={type:r.LogicalExpression,operator:"&&",left:e,right:wt()};return e}function St(){var e=Et();while(V("||"))F(),e={type:r.LogicalExpression,operator:"||",left:e,right:Et()};return e}function xt(){var e,t,n;return e=St(),V("?")&&(F(),t=d.allowIn,d.allowIn=!0,n=Tt(),d.allowIn=t,W(":"),e={type:r.ConditionalExpression,test:e,consequent:n,alternate:Tt()}),e}function Tt(){var e,t;return e=I(),t=xt(),J()&&(Q(t)||U({},s.InvalidLHSInAssignment),a&&t.type===r.Identifier&&k(t.name)&&U(e,s.StrictLHSAssignment),t={type:r.AssignmentExpression,operator:F().value,left:t,right:Tt()}),t}function Nt(){var e=Tt();if(V(",")){e={type:r.SequenceExpression,expressions:[e]};while(f<h){if(!V(","))break;F(),e.expressions.push(Tt())}}return e}function Ct(){var e=[],t;while(f<h){if(V("}"))break;t=en();if(typeof t=="undefined")break;e.push(t)}return e}function kt(){var e;return W("{"),e=Ct(),W("}"),{type:r.BlockStatement,body:e}}function Lt(){var e=F();return e.type!==t.Identifier&&z(e),{type:r.Identifier,name:e.value}}function At(e){var t=Lt(),n=null;return a&&k(t.name)&&U({},s.StrictVarName),e==="const"?(W("="),n=Tt()):V("=")&&(F(),n=Tt()),{type:r.VariableDeclarator,id:t,init:n}}function Ot(e){var t=[];do{t.push(At(e));if(!V(","))break;F()}while(f<h);return t}function Mt(){var e;return X("var"),e=Ot(),K(),{type:r.VariableDeclaration,declarations:e,kind:"var"}}function _t(e){var t;return X(e),t=Ot(e),K(),{type:r.VariableDeclaration,declarations:t,kind:e}}function Dt(){return W(";"),{type:r.EmptyStatement}}function Pt(){var e=Nt();return K(),{type:r.ExpressionStatement,expression:e}}function Ht(){var e,t,n;return X("if"),W("("),e=Nt(),W(")"),t=Qt(),$("else")?(F(),n=Qt()):n=null,{type:r.IfStatement,test:e,consequent:t,alternate:n}}function Bt(){var e,t,n;return X("do"),n=d.inIteration,d.inIteration=!0,e=Qt(),d.inIteration=n,X("while"),W("("),t=Nt(),W(")"),V(";")&&F(),{type:r.DoWhileStatement,body:e,test:t}}function jt(){var e,t,n;return X("while"),W("("),e=Nt(),W(")"),n=d.inIteration,d.inIteration=!0,t=Qt(),d.inIteration=n,{type:r.WhileStatement,test:e,body:t}}function Ft(){var e=F();return{type:r.VariableDeclaration,declarations:Ot(),kind:e.value}}function It(){var e,t,n,i,o,u,a;return e=t=n=null,X("for"),W("("),V(";")?F():($("var")||$("let")?(d.allowIn=!1,e=Ft(),d.allowIn=!0,e.declarations.length===1&&$("in")&&(F(),i=e,o=Nt(),e=null)):(d.allowIn=!1,e=Nt(),d.allowIn=!0,$("in")&&(Q(e)||U({},s.InvalidLHSInForIn),F(),i=e,o=Nt(),e=null)),typeof i=="undefined"&&W(";")),typeof i=="undefined"&&(V(";")||(t=Nt()),W(";"),V(")")||(n=Nt())),W(")"),a=d.inIteration,d.inIteration=!0,u=Qt(),d.inIteration=a,typeof i=="undefined"?{type:r.ForStatement,init:e,test:t,update:n,body:u}:{type:r.ForInStatement,left:i,right:o,body:u,each:!1}}function qt(){var e,n=null;return X("continue"),u[f]===";"?(F(),d.inIteration||R({},s.IllegalContinue),{type:r.ContinueStatement,label:null}):q()?(d.inIteration||R({},s.IllegalContinue),{type:r.ContinueStatement,label:null}):(e=I(),e.type===t.Identifier&&(n=Lt(),Object.prototype.hasOwnProperty.call(d.labelSet,n.name)||R({},s.UnknownLabel,n.name)),K(),n===null&&!d.inIteration&&R({},s.IllegalContinue),{type:r.ContinueStatement,label:n})}function Rt(){var e,n=null;return X("break"),u[f]===";"?(F(),!d.inIteration&&!d.inSwitch&&R({},s.IllegalBreak),{type:r.BreakStatement,label:null}):q()?(!d.inIteration&&!d.inSwitch&&R({},s.IllegalBreak),{type:r.BreakStatement,label:null}):(e=I(),e.type===t.Identifier&&(n=Lt(),Object.prototype.hasOwnProperty.call(d.labelSet,n.name)||R({},s.UnknownLabel,n.name)),K(),n===null&&!d.inIteration&&!d.inSwitch&&R({},s.IllegalBreak),{type:r.BreakStatement,label:n})}function Ut(){var e,n=null;return X("return"),d.inFunctionBody||U({},s.IllegalReturn),u[f]===" "&&x(u[f+1])?(n=Nt(),K(),{type:r.ReturnStatement,argument:n}):q()?{type:r.ReturnStatement,argument:null}:(V(";")||(e=I(),!V("}")&&e.type!==t.EOF&&(n=Nt())),K(),{type:r.ReturnStatement,argument:n})}function zt(){var e,t;return a&&U({},s.StrictModeWith),X("with"),W("("),e=Nt(),W(")"),t=Qt(),{type:r.WithStatement,object:e,body:t}}function Wt(){var e,t=[],n;$("default")?(F(),e=null):(X("case"),e=Nt()),W(":");while(f<h){if(V("}")||$("default")||$("case"))break;n=Qt();if(typeof n=="undefined")break;t.push(n)}return{type:r.SwitchCase,test:e,consequent:t}}function Xt(){var e,t,n,i,o;X("switch"),W("("),e=Nt(),W(")"),W("{"),t=[];if(V("}"))return F(),{type:r.SwitchStatement,discriminant:e,cases:t};i=d.inSwitch,d.inSwitch=!0,o=!1;while(f<h){if(V("}"))break;n=Wt(),n.test===null&&(o&&R({},s.MultipleDefaultsInSwitch),o=!0),t.push(n)}return d.inSwitch=i,W("}"),{type:r.SwitchStatement,discriminant:e,cases:t}}function Vt(){var e;return X("throw"),q()&&R({},s.NewlineAfterThrow),e=Nt(),K(),{type:r.ThrowStatement,argument:e}}function $t(){var e;return X("catch"),W("("),V(")")&&z(I()),e=Lt(),a&&k(e.name)&&U({},s.StrictCatchVariable),W(")"),{type:r.CatchClause,param:e,body:kt()}}function Jt(){var e,t=[],n=null;return X("try"),e=kt(),$("catch")&&t.push($t()),$("finally")&&(F(),n=kt()),t.length===0&&!n&&R({},s.NoCatchOrFinally),{type:r.TryStatement,block:e,guardedHandlers:[],handlers:t,finalizer:n}}function Kt(){return X("debugger"),K(),{type:r.DebuggerStatement}}function Qt(){var e=I(),n,i;e.type===t.EOF&&z(e);if(e.type===t.Punctuator)switch(e.value){case";":return Dt();case"{":return kt();case"(":return Pt();default:}if(e.type===t.Keyword)switch(e.value){case"break":return Rt();case"continue":return qt();case"debugger":return Kt();case"do":return Bt();case"for":return It();case"function":return Yt();case"if":return Ht();case"return":return Ut();case"switch":return Xt();case"throw":return Vt();case"try":return Jt();case"var":return Mt();case"while":return jt();case"with":return zt();default:}return n=Nt(),n.type===r.Identifier&&V(":")?(F(),Object.prototype.hasOwnProperty.call(d.labelSet,n.name)&&R({},s.Redeclaration,"Label",n.name),d.labelSet[n.name]=!0,i=Qt(),delete d.labelSet[n.name],{type:r.LabeledStatement,label:n,body:i}):(K(),{type:r.ExpressionStatement,expression:n})}function Gt(){var e,n=[],i,o,u,l,c,p,v;W("{");while(f<h){i=I();if(i.type!==t.StringLiteral)break;e=en(),n.push(e);if(e.expression.type!==r.Literal)break;o=g(i.range[0]+1,i.range[1]-1),o==="use strict"?(a=!0,u&&U(u,s.StrictOctalLiteral)):!u&&i.octal&&(u=i)}l=d.labelSet,c=d.inIteration,p=d.inSwitch,v=d.inFunctionBody,d.labelSet={},d.inIteration=!1,d.inSwitch=!1,d.inFunctionBody=!0;while(f<h){if(V("}"))break;e=en();if(typeof e=="undefined")break;n.push(e)}return W("}"),d.labelSet=l,d.inIteration=c,d.inSwitch=p,d.inFunctionBody=v,{type:r.BlockStatement,body:n}}function Yt(){var e,t,n=[],i,o,u,l,c,p,d;X("function"),o=I(),e=Lt(),a?k(o.value)&&U(o,s.StrictFunctionName):k(o.value)?(l=o,c=s.StrictFunctionName):C(o.value)&&(l=o,c=s.StrictReservedWord),W("(");if(!V(")")){d={};while(f<h){o=I(),t=Lt(),a?(k(o.value)&&(u=o,c=s.StrictParamName),Object.prototype.hasOwnProperty.call(d,o.value)&&(u=o,c=s.StrictParamDupe)):l||(k(o.value)?(l=o,c=s.StrictParamName):C(o.value)?(l=o,c=s.StrictReservedWord):Object.prototype.hasOwnProperty.call(d,o.value)&&(l=o,c=s.StrictParamDupe)),n.push(t),d[t.name]=!0;if(V(")"))break;W(",")}}return W(")"),p=a,i=Gt(),a&&l&&R(l,c),a&&u&&U(u,c),a=p,{type:r.FunctionDeclaration,id:e,params:n,defaults:[],body:i,rest:null,generator:!1,expression:!1}}function Zt(){var e,t=null,n,i,o,u,l=[],c,p,d;X("function"),V("(")||(e=I(),t=Lt(),a?k(e.value)&&U(e,s.StrictFunctionName):k(e.value)?(i=e,o=s.StrictFunctionName):C(e.value)&&(i=e,o=s.StrictReservedWord)),W("(");if(!V(")")){d={};while(f<h){e=I(),u=Lt(),a?(k(e.value)&&(n=e,o=s.StrictParamName),Object.prototype.hasOwnProperty.call(d,e.value)&&(n=e,o=s.StrictParamDupe)):i||(k(e.value)?(i=e,o=s.StrictParamName):C(e.value)?(i=e,o=s.StrictReservedWord):Object.prototype.hasOwnProperty.call(d,e.value)&&(i=e,o=s.StrictParamDupe)),l.push(u),d[u.name]=!0;if(V(")"))break;W(",")}}return W(")"),p=a,c=Gt(),a&&i&&R(i,o),a&&n&&U(n,o),a=p,{type:r.FunctionExpression,id:t,params:l,defaults:[],body:c,rest:null,generator:!1,expression:!1}}function en(){var e=I();if(e.type===t.Keyword)switch(e.value){case"const":case"let":return _t(e.value);case"function":return Yt();default:return Qt()}if(e.type!==t.EOF)return Qt()}function tn(){var e,n=[],i,o,u;while(f<h){i=I();if(i.type!==t.StringLiteral)break;e=en(),n.push(e);if(e.expression.type!==r.Literal)break;o=g(i.range[0]+1,i.range[1]-1),o==="use strict"?(a=!0,u&&U(u,s.StrictOctalLiteral)):!u&&i.octal&&(u=i)}while(f<h){e=en();if(typeof e=="undefined")break;n.push(e)}return n}function nn(){var e;return a=!1,e={type:r.Program,body:tn()},e}function rn(e,t,n,r,i){m(typeof n=="number","Comment must have valid position");if(v.comments.length>0&&v.comments[v.comments.length-1].range[1]>n)return;v.comments.push({type:e,value:t,range:[n,r],loc:i})}function sn(){var e,t,n,r,i,o;e="",i=!1,o=!1;while(f<h){t=u[f];if(o)t=u[f++],S(t)?(n.end={line:l,column:f-c-1},o=!1,rn("Line",e,r,f-1,n),t==="\r"&&u[f]==="\n"&&++f,++l,c=f,e=""):f>=h?(o=!1,e+=t,n.end={line:l,column:h-c},rn("Line",e,r,h,n)):e+=t;else if(i)S(t)?(t==="\r"&&u[f+1]==="\n"?(++f,e+="\r\n"):e+=t,++l,++f,c=f,f>=h&&R({},s.UnexpectedToken,"ILLEGAL")):(t=u[f++],f>=h&&R({},s.UnexpectedToken,"ILLEGAL"),e+=t,t==="*"&&(t=u[f],t==="/"&&(e=e.substr(0,e.length-1),i=!1,++f,n.end={line:l,column:f-c},rn("Block",e,r,f,n),e="")));else if(t==="/"){t=u[f+1];if(t==="/")n={start:{line:l,column:f-c}},r=f,f+=2,o=!0,f>=h&&(n.end={line:l,column:f-c},o=!1,rn("Line",e,r,f,n));else{if(t!=="*")break;r=f,f+=2,i=!0,n={start:{line:l,column:f-c-2}},f>=h&&R({},s.UnexpectedToken,"ILLEGAL")}}else if(E(t))++f;else{if(!S(t))break;++f,t==="\r"&&u[f]==="\n"&&++f,++l,c=f}}}function on(){var e,t,n,r=[];for(e=0;e<v.comments.length;++e)t=v.comments[e],n={type:t.type,value:t.value},v.range&&(n.range=t.range),v.loc&&(n.loc=t.loc),r.push(n);v.comments=r}function un(){var e,r,i,s,o;return A(),e=f,r={start:{line:l,column:f-c}},i=v.advance(),r.end={line:l,column:f-c},i.type!==t.EOF&&(s=[i.range[0],i.range[1]],o=g(i.range[0],i.range[1]),v.tokens.push({type:n[i.type],value:o,range:s,loc:r})),i}function an(){var e,t,n,r;return A(),e=f,t={start:{line:l,column:f-c}},n=v.scanRegExp(),t.end={line:l,column:f-c},v.tokens.length>0&&(r=v.tokens[v.tokens.length-1],r.range[0]===e&&r.type==="Punctuator"&&(r.value==="/"||r.value==="/=")&&v.tokens.pop()),v.tokens.push({type:"RegularExpression",value:n.literal,range:[e,f],loc:t}),n}function fn(){var e,t,n,r=[];for(e=0;e<v.tokens.length;++e)t=v.tokens[e],n={type:t.type,value:t.value},v.range&&(n.range=t.range),v.loc&&(n.loc=t.loc),r.push(n);v.tokens=r}function ln(e){return{type:r.Literal,value:e.value}}function cn(e){return{type:r.Literal,value:e.value,raw:g(e.range[0],e.range[1])}}function hn(){var e={};return e.range=[f,f],e.loc={start:{line:l,column:f-c},end:{line:l,column:f-c}},e.end=function(){this.range[1]=f,this.loc.end.line=l,this.loc.end.column=f-c},e.applyGroup=function(e){v.range&&(e.groupRange=[this.range[0],this.range[1]]),v.loc&&(e.groupLoc={start:{line:this.loc.start.line,column:this.loc.start.column},end:{line:this.loc.end.line,column:this.loc.end.column}})},e.apply=function(e){v.range&&(e.range=[this.range[0],this.range[1]]),v.loc&&(e.loc={start:{line:this.loc.start.line,column:this.loc.start.column},end:{line:this.loc.end.line,column:this.loc.end.column}})},e}function pn(){var e,t;return A(),e=hn(),W("("),t=Nt(),W(")"),e.end(),e.applyGroup(t),t}function dn(){var e,t;A(),e=hn(),t=$("new")?at():rt();while(V(".")||V("["))V("[")?(t={type:r.MemberExpression,computed:!0,object:t,property:ut()},e.end(),e.apply(t)):(t={type:r.MemberExpression,computed:!1,object:t,property:ot()},e.end(),e.apply(t));return t}function vn(){var e,t;A(),e=hn(),t=$("new")?at():rt();while(V(".")||V("[")||V("("))V("(")?(t={type:r.CallExpression,callee:t,arguments:it()},e.end(),e.apply(t)):V("[")?(t={type:r.MemberExpression,computed:!0,object:t,property:ut()},e.end(),e.apply(t)):(t={type:r.MemberExpression,computed:!1,object:t,property:ot()},e.end(),e.apply(t));return t}function mn(e){var t,n,r;t=Object.prototype.toString.apply(e)==="[object Array]"?[]:{};for(n in e)e.hasOwnProperty(n)&&n!=="groupRange"&&n!=="groupLoc"&&(r=e[n],r===null||typeof r!="object"||r instanceof RegExp?t[n]=r:t[n]=mn(r));return t}function gn(e,t){return function(n){function i(e){return e.type===r.LogicalExpression||e.type===r.BinaryExpression}function s(n){var r,o;i(n.left)&&s(n.left),i(n.right)&&s(n.right),e&&(n.left.groupRange||n.right.groupRange?(r=n.left.groupRange?n.left.groupRange[0]:n.left.range[0],o=n.right.groupRange?n.right.groupRange[1]:n.right.range[1],n.range=[r,o]):typeof n.range=="undefined"&&(r=n.left.range[0],o=n.right.range[1],n.range=[r,o])),t&&(n.left.groupLoc||n.right.groupLoc?(r=n.left.groupLoc?n.left.groupLoc.start:n.left.loc.start,o=n.right.groupLoc?n.right.groupLoc.end:n.right.loc.end,n.loc={start:r,end:o}):typeof n.loc=="undefined"&&(n.loc={start:n.left.loc.start,end:n.right.loc.end}))}return function(){var r,o;return A(),r=hn(),o=n.apply(null,arguments),r.end(),e&&typeof o.range=="undefined"&&r.apply(o),t&&typeof o.loc=="undefined"&&r.apply(o),i(o)&&s(o),o}}}function yn(){var e;v.comments&&(v.skipComment=A,A=sn),v.raw&&(v.createLiteral=ln,ln=cn);if(v.range||v.loc)v.parseGroupExpression=nt,v.parseLeftHandSideExpression=lt,v.parseLeftHandSideExpressionAllowCall=ft,nt=pn,lt=dn,ft=vn,e=gn(v.range,v.loc),v.parseAdditiveExpression=dt,v.parseAssignmentExpression=Tt,v.parseBitwiseANDExpression=yt,v.parseBitwiseORExpression=wt,v.parseBitwiseXORExpression=bt,v.parseBlock=kt,v.parseFunctionSourceElements=Gt,v.parseCatchClause=$t,v.parseComputedMember=ut,v.parseConditionalExpression=xt,v.parseConstLetDeclaration=_t,v.parseEqualityExpression=gt,v.parseExpression=Nt,v.parseForVariableDeclaration=Ft,v.parseFunctionDeclaration=Yt,v.parseFunctionExpression=Zt,v.parseLogicalANDExpression=Et,v.parseLogicalORExpression=St,v.parseMultiplicativeExpression=pt,v.parseNewExpression=at,v.parseNonComputedProperty=st,v.parseObjectProperty=et,v.parseObjectPropertyKey=Z,v.parsePostfixExpression=ct,v.parsePrimaryExpression=rt,v.parseProgram=nn,v.parsePropertyFunction=Y,v.parseRelationalExpression=mt,v.parseStatement=Qt,v.parseShiftExpression=vt,v.parseSwitchCase=Wt,v.parseUnaryExpression=ht,v.parseVariableDeclaration=At,v.parseVariableIdentifier=Lt,dt=e(v.parseAdditiveExpression),Tt=e(v.parseAssignmentExpression),yt=e(v.parseBitwiseANDExpression),wt=e(v.parseBitwiseORExpression),bt=e(v.parseBitwiseXORExpression),kt=e(v.parseBlock),Gt=e(v.parseFunctionSourceElements),$t=e(v.parseCatchClause),ut=e(v.parseComputedMember),xt=e(v.parseConditionalExpression),_t=e(v.parseConstLetDeclaration),gt=e(v.parseEqualityExpression),Nt=e(v.parseExpression),Ft=e(v.parseForVariableDeclaration),Yt=e(v.parseFunctionDeclaration),Zt=e(v.parseFunctionExpression),lt=e(lt),Et=e(v.parseLogicalANDExpression),St=e(v.parseLogicalORExpression),pt=e(v.parseMultiplicativeExpression),at=e(v.parseNewExpression),st=e(v.parseNonComputedProperty),et=e(v.parseObjectProperty),Z=e(v.parseObjectPropertyKey),ct=e(v.parsePostfixExpression),rt=e(v.parsePrimaryExpression),nn=e(v.parseProgram),Y=e(v.parsePropertyFunction),mt=e(v.parseRelationalExpression),Qt=e(v.parseStatement),vt=e(v.parseShiftExpression),Wt=e(v.parseSwitchCase),ht=e(v.parseUnaryExpression),At=e(v.parseVariableDeclaration),Lt=e(v.parseVariableIdentifier);typeof v.tokens!="undefined"&&(v.advance=j,v.scanRegExp=H,j=un,H=an)}function bn(){typeof v.skipComment=="function"&&(A=v.skipComment),v.raw&&(ln=v.createLiteral);if(v.range||v.loc)dt=v.parseAdditiveExpression,Tt=v.parseAssignmentExpression,yt=v.parseBitwiseANDExpression,wt=v.parseBitwiseORExpression,bt=v.parseBitwiseXORExpression,kt=v.parseBlock,Gt=v.parseFunctionSourceElements,$t=v.parseCatchClause,ut=v.parseComputedMember,xt=v.parseConditionalExpression,_t=v.parseConstLetDeclaration,gt=v.parseEqualityExpression,Nt=v.parseExpression,Ft=v.parseForVariableDeclaration,Yt=v.parseFunctionDeclaration,Zt=v.parseFunctionExpression,nt=v.parseGroupExpression,lt=v.parseLeftHandSideExpression,ft=v.parseLeftHandSideExpressionAllowCall,Et=v.parseLogicalANDExpression,St=v.parseLogicalORExpression,pt=v.parseMultiplicativeExpression,at=v.parseNewExpression,st=v.parseNonComputedProperty,et=v.parseObjectProperty,Z=v.parseObjectPropertyKey,rt=v.parsePrimaryExpression,ct=v.parsePostfixExpression,nn=v.parseProgram,Y=v.parsePropertyFunction,mt=v.parseRelationalExpression,Qt=v.parseStatement,vt=v.parseShiftExpression,Wt=v.parseSwitchCase,ht=v.parseUnaryExpression,At=v.parseVariableDeclaration,Lt=v.parseVariableIdentifier;typeof v.scanRegExp=="function"&&(j=v.advance,H=v.scanRegExp)}function wn(e){var t=e.length,n=[],r;for(r=0;r<t;++r)n[r]=e.charAt(r);return n}function En(e,t){var n,r;r=String,typeof e!="string"&&!(e instanceof String)&&(e=r(e)),u=e,f=0,l=u.length>0?1:0,c=0,h=u.length,p=null,d={allowIn:!0,labelSet:{},inFunctionBody:!1,inIteration:!1,inSwitch:!1},v={},typeof t!="undefined"&&(v.range=typeof t.range=="boolean"&&t.range,v.loc=typeof t.loc=="boolean"&&t.loc,v.raw=typeof t.raw=="boolean"&&t.raw,typeof t.tokens=="boolean"&&t.tokens&&(v.tokens=[]),typeof t.comment=="boolean"&&t.comment&&(v.comments=[]),typeof t.tolerant=="boolean"&&t.tolerant&&(v.errors=[])),h>0&&typeof u[0]=="undefined"&&(e instanceof String&&(u=e.valueOf()),typeof u[0]=="undefined"&&(u=wn(e))),yn();try{n=nn(),typeof v.comments!="undefined"&&(on(),n.comments=v.comments),typeof v.tokens!="undefined"&&(fn(),n.tokens=v.tokens),typeof v.errors!="undefined"&&(n.errors=v.errors);if(v.range||v.loc)n.body=mn(n.body)}catch(i){throw i}finally{bn(),v={}}return n}var t,n,r,i,s,o,u,a,f,l,c,h,p,d,v;t={BooleanLiteral:1,EOF:2,Identifier:3,Keyword:4,NullLiteral:5,NumericLiteral:6,Punctuator:7,StringLiteral:8},n={},n[t.BooleanLiteral]="Boolean",n[t.EOF]="<end>",n[t.Identifier]="Identifier",n[t.Keyword]="Keyword",n[t.NullLiteral]="Null",n[t.NumericLiteral]="Numeric",n[t.Punctuator]="Punctuator",n[t.StringLiteral]="String",r={AssignmentExpression:"AssignmentExpression",ArrayExpression:"ArrayExpression",BlockStatement:"BlockStatement",BinaryExpression:"BinaryExpression",BreakStatement:"BreakStatement",CallExpression:"CallExpression",CatchClause:"CatchClause",ConditionalExpression:"ConditionalExpression",ContinueStatement:"ContinueStatement",DoWhileStatement:"DoWhileStatement",DebuggerStatement:"DebuggerStatement",EmptyStatement:"EmptyStatement",ExpressionStatement:"ExpressionStatement",ForStatement:"ForStatement",ForInStatement:"ForInStatement",FunctionDeclaration:"FunctionDeclaration",FunctionExpression:"FunctionExpression",Identifier:"Identifier",IfStatement:"IfStatement",Literal:"Literal",LabeledStatement:"LabeledStatement",LogicalExpression:"LogicalExpression",MemberExpression:"MemberExpression",NewExpression:"NewExpression",ObjectExpression:"ObjectExpression",Program:"Program",Property:"Property",ReturnStatement:"ReturnStatement",SequenceExpression:"SequenceExpression",SwitchStatement:"SwitchStatement",SwitchCase:"SwitchCase",ThisExpression:"ThisExpression",ThrowStatement:"ThrowStatement",TryStatement:"TryStatement",UnaryExpression:"UnaryExpression",UpdateExpression:"UpdateExpression",VariableDeclaration:"VariableDeclaration",VariableDeclarator:"VariableDeclarator",WhileStatement:"WhileStatement",WithStatement:"WithStatement"},i={Data:1,Get:2,Set:4},s={UnexpectedToken:"Unexpected token %0",UnexpectedNumber:"Unexpected number",UnexpectedString:"Unexpected string",UnexpectedIdentifier:"Unexpected identifier",UnexpectedReserved:"Unexpected reserved word",UnexpectedEOS:"Unexpected end of input",NewlineAfterThrow:"Illegal newline after throw",InvalidRegExp:"Invalid regular expression",UnterminatedRegExp:"Invalid regular expression: missing /",InvalidLHSInAssignment:"Invalid left-hand side in assignment",InvalidLHSInForIn:"Invalid left-hand side in for-in",MultipleDefaultsInSwitch:"More than one default clause in switch statement",NoCatchOrFinally:"Missing catch or finally after try",UnknownLabel:"Undefined label '%0'",Redeclaration:"%0 '%1' has already been declared",IllegalContinue:"Illegal continue statement",IllegalBreak:"Illegal break statement",IllegalReturn:"Illegal return statement",StrictModeWith:"Strict mode code may not include a with statement",StrictCatchVariable:"Catch variable may not be eval or arguments in strict mode",StrictVarName:"Variable name may not be eval or arguments in strict mode",StrictParamName:"Parameter name eval or arguments is not allowed in strict mode",StrictParamDupe:"Strict mode function may not have duplicate parameter names",StrictFunctionName:"Function name may not be eval or arguments in strict mode",StrictOctalLiteral:"Octal literals are not allowed in strict mode.",StrictDelete:"Delete of an unqualified identifier in strict mode.",StrictDuplicateProperty:"Duplicate data property in object literal not allowed in strict mode",AccessorDataProperty:"Object literal may not have data and accessor property with the same name",AccessorGetSet:"Object literal may not have multiple get/set accessors with the same name",StrictLHSAssignment:"Assignment to eval or arguments is not allowed in strict mode",StrictLHSPostfix:"Postfix increment/decrement may not have eval or arguments operand in strict mode",StrictLHSPrefix:"Prefix increment/decrement may not have eval or arguments operand in strict mode",StrictReservedWord:"Use of future reserved word in strict mode"},o={NonAsciiIdentifierStart:new RegExp("[\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]"),NonAsciiIdentifierPart:new RegExp("[\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]")},typeof "esprima"[0]=="undefined"&&(g=function(t,n){return u.slice(t,n).join("")}),e.version="1.0.4",e.parse=En,e.Syntax=function(){var e,t={};typeof Object.create=="function"&&(t=Object.create(null));for(e in r)r.hasOwnProperty(e)&&(t[e]=r[e]);return typeof Object.freeze=="function"&&Object.freeze(t),t}()})})(null),function(e,t){function o(e,t,n){function o(t){n[e.range[0]]=t;for(var r=e.range[0]+1;r<e.range[1];r++)n[r]=""}if(!e.range)return;e.parent=t,e.source=function(){return n.slice(e.range[0],e.range[1]).join("")};if(e.update&&typeof e.update=="object"){var s=e.update;i(r(s),function(e){o[e]=s[e]}),e.update=o}else e.update=o}var n=e("esprima").parse,r=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t},i=function(e,t){if(e.forEach)return e.forEach(t);for(var n=0;n<e.length;n++)t.call(e,e[n],n,e)},s=Array.isArray||function(e){return Object.prototype.toString.call(e)==="[object Array]"};t.exports=function(e,t,u){typeof t=="function"&&(u=t,t={}),typeof e=="object"&&(t=e,e=t.source,delete t.source),e=e===undefined?t.source:e,t.range=!0,typeof e!="string"&&(e=String(e));var a=n(e,t),f={chunks:e.split(""),toString:function(){return f.chunks.join("")},inspect:function(){return f.toString()}},l=0;return function c(e,t){o(e,t,f.chunks),i(r(e),function(t){if(t==="parent")return;var n=e[t];s(n)?i(n,function(t){t&&typeof t.type=="string"&&c(t,e)}):n&&typeof n.type=="string"&&(o(n,e,f.chunks),c(n,e))}),u(e)}(a,undefined),f},window.falafel=t.exports}(function(){return{parse:esprima.parse}},{exports:{}});var inBrowser=typeof window!="undefined"&&this===window,parseAndModify=inBrowser?window.falafel:require("falafel");(inBrowser?window:exports).blanket=function(){var e=["ExpressionStatement","BreakStatement","ContinueStatement","VariableDeclaration","ReturnStatement","ThrowStatement","TryStatement","FunctionDeclaration","IfStatement","WhileStatement","DoWhileStatement","ForStatement","ForInStatement","SwitchStatement","WithStatement"],t=["IfStatement","WhileStatement","DoWhileStatement","ForStatement","ForInStatement","WithStatement"],n,r=Math.floor(Math.random()*1e3),i={},s={reporter:null,adapter:null,filter:null,customVariable:null,loader:null,ignoreScriptError:!1,existingRequireJS:!1,autoStart:!1,timeout:180,ignoreCors:!1,branchTracking:!1,sourceURL:!1,debug:!1,engineOnly:!1,testReadyCallback:null,commonJS:!1,instrumentCache:!1,modulePattern:null};return inBrowser&&typeof window.blanket!="undefined"&&(n=window.blanket.noConflict()),_blanket={noConflict:function(){return n?n:_blanket},_getCopyNumber:function(){return r},extend:function(e){_blanket._extend(_blanket,e)},_extend:function(e,t){if(t)for(var n in t)e[n]instanceof Object&&typeof e[n]!="function"?_blanket._extend(e[n],t[n]):e[n]=t[n]},getCovVar:function(){var e=_blanket.options("customVariable");return e?(_blanket.options("debug")&&console.log("BLANKET-Using custom tracking variable:",e),inBrowser?"window."+e:e):inBrowser?"window._$blanket":"_$jscoverage"},options:function(e,t){if(typeof e!="string")_blanket._extend(s,e);else{if(typeof t=="undefined")return s[e];s[e]=t}},instrument:function(e,t){var n=e.inputFile,r=e.inputFileName;if(_blanket.options("instrumentCache")&&sessionStorage&&sessionStorage.getItem("blanket_instrument_store-"+r))_blanket.options("debug")&&console.log("BLANKET-Reading instrumentation from cache: ",r),t(sessionStorage.getItem("blanket_instrument_store-"+r));else{var i=_blanket._prepareSource(n);_blanket._trackingArraySetup=[];var s=parseAndModify(n,{loc:!0,comment:!0},_blanket._addTracking(r));s=_blanket._trackingSetup(r,i)+s,_blanket.options("sourceURL")&&(s+="\n//@ sourceURL="+r.replace("http://","")),_blanket.options("debug")&&console.log("BLANKET-Instrumented file: ",r),_blanket.options("instrumentCache")&&sessionStorage&&(_blanket.options("debug")&&console.log("BLANKET-Saving instrumentation to cache: ",r),sessionStorage.setItem("blanket_instrument_store-"+r,s)),t(s)}},_trackingArraySetup:[],_branchingArraySetup:[],_prepareSource:function(e){return e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/gm,"\n").split("\n")},_trackingSetup:function(e,t){var n=_blanket.options("branchTracking"),r=t.join("',\n'"),i="",s=_blanket.getCovVar();return i+="if (typeof "+s+" === 'undefined') "+s+" = {};\n",n&&(i+="var _$branchFcn=function(f,l,c,r){ ",i+="if (!!r) { ",i+=s+"[f].branchData[l][c][0] = "+s+"[f].branchData[l][c][0] || [];",i+=s+"[f].branchData[l][c][0].push(r); }",i+="else { ",i+=s+"[f].branchData[l][c][1] = "+s+"[f].branchData[l][c][1] || [];",i+=s+"[f].branchData[l][c][1].push(r); }",i+="return r;};\n"),i+="if (typeof "+s+"['"+e+"'] === 'undefined'){",i+=s+"['"+e+"']=[];\n",n&&(i+=s+"['"+e+"'].branchData=[];\n"),i+=s+"['"+e+"'].source=['"+r+"'];\n",_blanket._trackingArraySetup.sort(function(e,t){return parseInt(e,10)>parseInt(t,10)}).forEach(function(t){i+=s+"['"+e+"']["+t+"]=0;\n"}),n&&_blanket._branchingArraySetup.sort(function(e,t){return e.line>t.line}).sort(function(e,t){return e.column>t.column}).forEach(function(t){t.file===e&&(i+="if (typeof "+s+"['"+e+"'].branchData["+t.line+"] === 'undefined'){\n",i+=s+"['"+e+"'].branchData["+t.line+"]=[];\n",i+="}",i+=s+"['"+e+"'].branchData["+t.line+"]["+t.column+"] = [];\n",i+=s+"['"+e+"'].branchData["+t.line+"]["+t.column+"].consequent = "+JSON.stringify(t.consequent)+";\n",i+=s+"['"+e+"'].branchData["+t.line+"]["+t.column+"].alternate = "+JSON.stringify(t.alternate)+";\n")}),i+="}",i},_blockifyIf:function(e){if(t.indexOf(e.type)>-1){var n=e.consequent||e.body,r=e.alternate;r&&r.type!=="BlockStatement"&&r.update("{\n"+r.source()+"}\n"),n&&n.type!=="BlockStatement"&&n.update("{\n"+n.source()+"}\n")}},_trackBranch:function(e,t){var n=e.loc.start.line,r=e.loc.start.column;_blanket._branchingArraySetup.push({line:n,column:r,file:t,consequent:e.consequent.loc,alternate:e.alternate.loc});var i="_$branchFcn('"+t+"',"+n+","+r+","+e.test.source()+")?"+e.consequent.source()+":"+e.alternate.source();e.update(i)},_addTracking:function(t){var n=_blanket.getCovVar();return function(r){_blanket._blockifyIf(r);if(e.indexOf(r.type)>-1&&r.parent.type!=="LabeledStatement"){_blanket._checkDefs(r,t);if(r.type==="VariableDeclaration"&&(r.parent.type==="ForStatement"||r.parent.type==="ForInStatement"))return;if(!r.loc||!r.loc.start)throw new Error("The instrumenter encountered a node with no location: "+Object.keys(r));r.update(n+"['"+t+"']["+r.loc.start.line+"]++;\n"+r.source()),_blanket._trackingArraySetup.push(r.loc.start.line)}else _blanket.options("branchTracking")&&r.type==="ConditionalExpression"&&_blanket._trackBranch(r,t)}},_checkDefs:function(e,t){if(inBrowser){e.type==="VariableDeclaration"&&e.declarations&&e.declarations.forEach(function(n){if(n.id.name==="window")throw new Error("Instrumentation error, you cannot redefine the 'window' variable in  "+t+":"+e.loc.start.line)}),e.type==="FunctionDeclaration"&&e.params&&e.params.forEach(function(n){if(n.name==="window")throw new Error("Instrumentation error, you cannot redefine the 'window' variable in  "+t+":"+e.loc.start.line)});if(e.type==="ExpressionStatement"&&e.expression&&e.expression.left&&e.expression.left.object&&e.expression.left.property&&e.expression.left.object.name+"."+e.expression.left.property.name===_blanket.getCovVar())throw new Error("Instrumentation error, you cannot redefine the coverage variable in  "+t+":"+e.loc.start.line)}else if(e.type==="ExpressionStatement"&&e.expression&&e.expression.left&&!e.expression.left.object&&!e.expression.left.property&&e.expression.left.name===_blanket.getCovVar())throw new Error("Instrumentation error, you cannot redefine the coverage variable in  "+t+":"+e.loc.start.line)},setupCoverage:function(){i.instrumentation="blanket",i.stats={suites:0,tests:0,passes:0,pending:0,failures:0,start:new Date}},_checkIfSetup:function(){if(!i.stats)throw new Error("You must call blanket.setupCoverage() first.")},onTestStart:function(){_blanket.options("debug")&&console.log("BLANKET-Test event started"),this._checkIfSetup(),i.stats.tests++,i.stats.pending++},onTestDone:function(e,t){this._checkIfSetup(),t===e?i.stats.passes++:i.stats.failures++,i.stats.pending--},onModuleStart:function(){this._checkIfSetup(),i.stats.suites++},onTestsDone:function(){_blanket.options("debug")&&console.log("BLANKET-Test event done"),this._checkIfSetup(),i.stats.end=new Date,inBrowser?this.report(i):(_blanket.options("branchTracking")||delete (inBrowser?window:global)[_blanket.getCovVar()].branchFcn,this.options("reporter").call(this,i))}},_blanket}(),function(e){var t=e.options;e.extend({outstandingRequireFiles:[],options:function(n,r){var i={};if(typeof n!="string")t(n),i=n;else{if(typeof r=="undefined")return t(n);t(n,r),i[n]=r}i.adapter&&e._loadFile(i.adapter),i.loader&&e._loadFile(i.loader)},requiringFile:function(t,n){typeof t=="undefined"?e.outstandingRequireFiles=[]:typeof n=="undefined"?e.outstandingRequireFiles.push(t):e.outstandingRequireFiles.splice(e.outstandingRequireFiles.indexOf(t),1)},requireFilesLoaded:function(){return e.outstandingRequireFiles.length===0},showManualLoader:function(){if(document.getElementById("blanketLoaderDialog"))return;var e="<div class='blanketDialogOverlay'>";e+="&nbsp;</div>",e+="<div class='blanketDialogVerticalOffset'>",e+="<div class='blanketDialogBox'>",e+="<b>Error:</b> Blanket.js encountered a cross origin request error while instrumenting the source files.  ",e+="<br><br>This is likely caused by the source files being referenced locally (using the file:// protocol). ",e+="<br><br>Some solutions include <a href='http://askubuntu.com/questions/160245/making-google-chrome-option-allow-file-access-from-files-permanent' target='_blank'>starting Chrome with special flags</a>, <a target='_blank' href='https://github.com/remy/servedir'>running a server locally</a>, or using a browser without these CORS restrictions (Safari).",e+="<br>",typeof FileReader!="undefined"&&(e+="<br>Or, try the experimental loader.  When prompted, simply click on the directory containing all the source files you want covered.",e+="<a href='javascript:document.getElementById(\"fileInput\").click();'>Start Loader</a>",e+="<input type='file' type='application/x-javascript' accept='application/x-javascript' webkitdirectory id='fileInput' multiple onchange='window.blanket.manualFileLoader(this.files)' style='visibility:hidden;position:absolute;top:-50;left:-50'/>"),e+="<br><span style='float:right;cursor:pointer;'  onclick=document.getElementById('blanketLoaderDialog').style.display='none';>Close</span>",e+="<div style='clear:both'></div>",e+="</div></div>";var t=".blanketDialogWrapper {";t+="display:block;",t+="position:fixed;",t+="z-index:40001; }",t+=".blanketDialogOverlay {",t+="position:fixed;",t+="width:100%;",t+="height:100%;",t+="background-color:black;",t+="opacity:.5; ",t+="-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)'; ",t+="filter:alpha(opacity=50); ",t+="z-index:40001; }",t+=".blanketDialogVerticalOffset { ",t+="position:fixed;",t+="top:30%;",t+="width:100%;",t+="z-index:40002; }",t+=".blanketDialogBox { ",t+="width:405px; ",t+="position:relative;",t+="margin:0 auto;",t+="background-color:white;",t+="padding:10px;",t+="border:1px solid black; }";var n=document.createElement("style");n.innerHTML=t,document.head.appendChild(n);var r=document.createElement("div");r.id="blanketLoaderDialog",r.className="blanketDialogWrapper",r.innerHTML=e,document.body.insertBefore(r,document.body.firstChild)},manualFileLoader:function(e){function o(e){var t=new FileReader;t.onload=s,t.readAsText(e)}var t=Array.prototype.slice;e=t.call(e).filter(function(e){return e.type!==""});var n=e.length-1,r=0,i={};sessionStorage.blanketSessionLoader&&(i=JSON.parse(sessionStorage.blanketSessionLoader));var s=function(t){var s=t.currentTarget.result,u=e[r],a=u.webkitRelativePath&&u.webkitRelativePath!==""?u.webkitRelativePath:u.name;i[a]=s,r++,r===n?(sessionStorage.setItem("blanketSessionLoader",JSON.stringify(i)),document.location.reload()):o(e[r])};o(e[r])},_loadFile:function(t){if(typeof t!="undefined"){var n=new XMLHttpRequest;n.open("GET",t,!1),n.send(),e._addScript(n.responseText)}},_addScript:function(e){var t=document.createElement("script");t.type="text/javascript",t.text=e,(document.body||document.getElementsByTagName("head")[0]).appendChild(t)},hasAdapter:function(t){return e.options("adapter")!==null},report:function(t){document.getElementById("blanketLoaderDialog")||(e.blanketSession=null),t.files=window._$blanket;var n=blanket.options("commonJS")?blanket._commonjs.require:window.require;if(!t.files||!Object.keys(t.files).length){e.options("debug")&&console.log("BLANKET-Reporting No files were instrumented.");return}typeof t.files.branchFcn!="undefined"&&delete t.files.branchFcn;if(typeof e.options("reporter")=="string")e._loadFile(e.options("reporter")),e.customReporter(t,e.options("reporter_options"));else if(typeof e.options("reporter")=="function")e.options("reporter")(t,e.options("reporter_options"));else{if(typeof e.defaultReporter!="function")throw new Error("no reporter defined.");e.defaultReporter(t,e.options("reporter_options"))}},_bindStartTestRunner:function(e,t){e?e(t):window.addEventListener("load",t,!1)},_loadSourceFiles:function(t){function r(e){var t=Object.create(Object.getPrototypeOf(e)),n=Object.getOwnPropertyNames(e);return n.forEach(function(n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r)}),t}var n=blanket.options("commonJS")?blanket._commonjs.require:window.require;e.options("debug")&&console.log("BLANKET-Collecting page scripts");var i=e.utils.collectPageScripts();if(i.length===0)t();else{sessionStorage.blanketSessionLoader&&(e.blanketSession=JSON.parse(sessionStorage.blanketSessionLoader)),i.forEach(function(t,n){e.utils.cache[t]={loaded:!1}});var s=-1;e.utils.loadAll(function(e){return e?typeof i[s+1]!="undefined":(s++,s>=i.length?null:i[s])},t)}},beforeStartTestRunner:function(t){t=t||{},t.checkRequirejs=typeof t.checkRequirejs=="undefined"?!0:t.checkRequirejs,t.callback=t.callback||function(){},t.coverage=typeof t.coverage=="undefined"?!0:t.coverage,t.coverage?e._bindStartTestRunner(t.bindEvent,function(){e._loadSourceFiles(function(){var n=function(){return t.condition?t.condition():e.requireFilesLoaded()},r=function(){if(n()){e.options("debug")&&console.log("BLANKET-All files loaded, init start test runner callback.");var i=e.options("testReadyCallback");i?typeof i=="function"?i(t.callback):typeof i=="string"&&(e._addScript(i),t.callback()):t.callback()}else setTimeout(r,13)};r()})}):t.callback()},utils:{qualifyURL:function(e){var t=document.createElement("a");return t.href=e,t.href}}})}(blanket),blanket.defaultReporter=function(e){function l(e){var t=document.getElementById(e);t.style.display==="block"?t.style.display="none":t.style.display="block"}function d(e){return e.replace(/\&/g,"&amp;").replace(/</g,"&lt;").replace(/\>/g,"&gt;").replace(/\"/g,"&quot;").replace(/\'/g,"&apos;")}function v(e,t){var n=t?0:1;return typeof e=="undefined"||typeof e===null||typeof e[n]=="undefined"?!1:e[n].length>0}function g(e,t,n,r,i){var s="",o="";if(m.length>0){s+="<span class='"+(v(m[0][1],m[0][1].consequent===m[0][0])?"branchOkay":"branchWarning")+"'>";if(m[0][0].end.line===i){s+=d(t.slice(0,m[0][0].end.column))+"</span>",t=t.slice(m[0][0].end.column),m.shift();if(m.length>0){s+="<span class='"+(v(m[0][1],!1)?"branchOkay":"branchWarning")+"'>";if(m[0][0].end.line===i){s+=d(t.slice(0,m[0][0].end.column))+"</span>",t=t.slice(m[0][0].end.column),m.shift();if(!n)return{src:s+d(t),cols:n}}else{if(!n)return{src:s+d(t)+"</span>",cols:n};o="</span>"}}else if(!n)return{src:s+d(t),cols:n}}else{if(!n)return{src:s+d(t)+"</span>",cols:n};o="</span>"}}var u=n[e],a=u.consequent;if(a.start.line>i)m.unshift([u.alternate,u]),m.unshift([a,u]),t=d(t);else{var f="<span class='"+(v(u,!0)?"branchOkay":"branchWarning")+"'>";s+=d(t.slice(0,a.start.column-r))+f;if(n.length>e+1&&n[e+1].consequent.start.line===i&&n[e+1].consequent.start.column-r<n[e].consequent.end.column-r){var l=g(e+1,t.slice(a.start.column-r,a.end.column-r),n,a.start.column-r,i);s+=l.src,n=l.cols,n[e+1]=n[e+2],n.length--}else s+=d(t.slice(a.start.column-r,a.end.column-r));s+="</span>";var c=u.alternate;if(c.start.line>i)s+=d(t.slice(a.end.column-r)),m.unshift([c,u]);else{s+=d(t.slice(a.end.column-r,c.start.column-r)),f="<span class='"+(v(u,!1)?"branchOkay":"branchWarning")+"'>",s+=f;if(n.length>e+1&&n[e+1].consequent.start.line===i&&n[e+1].consequent.start.column-r<n[e].alternate.end.column-r){var h=g(e+1,t.slice(c.start.column-r,c.end.column-r),n,c.start.column-r,i);s+=h.src,n=h.cols,n[e+1]=n[e+2],n.length--}else s+=d(t.slice(c.start.column-r,c.end.column-r));s+="</span>",s+=d(t.slice(c.end.column-r)),t=s}}return{src:t+o,cols:n}}var t="#blanket-main {margin:2px;background:#EEE;color:#333;clear:both;font-family:'Helvetica Neue Light', 'HelveticaNeue-Light', 'Helvetica Neue', Calibri, Helvetica, Arial, sans-serif; font-size:17px;} #blanket-main a {color:#333;text-decoration:none;}  #blanket-main a:hover {text-decoration:underline;} .blanket {margin:0;padding:5px;clear:both;border-bottom: 1px solid #FFFFFF;} .bl-error {color:red;}.bl-success {color:#5E7D00;} .bl-file{width:auto;} .bl-cl{float:left;} .blanket div.rs {margin-left:50px; width:150px; float:right} .bl-nb {padding-right:10px;} #blanket-main a.bl-logo {color: #EB1764;cursor: pointer;font-weight: bold;text-decoration: none} .bl-source{ overflow-x:scroll; background-color: #FFFFFF; border: 1px solid #CBCBCB; color: #363636; margin: 25px 20px; width: 80%;} .bl-source div{white-space: pre;font-family: monospace;} .bl-source > div > span:first-child{background-color: #EAEAEA;color: #949494;display: inline-block;padding: 0 10px;text-align: center;width: 30px;} .bl-source .miss{background-color:#e6c3c7} .bl-source span.branchWarning{color:#000;background-color:yellow;} .bl-source span.branchOkay{color:#000;background-color:transparent;}",n=60,r=document.head,i=0,s=document.body,o,u=Object.keys(e.files).some(function(t){return typeof e.files[t].branchData!="undefined"}),a="<div id='blanket-main'><div class='blanket bl-title'><div class='bl-cl bl-file'><a href='http://alex-seville.github.com/blanket/' target='_blank' class='bl-logo'>Blanket.js</a> results</div><div class='bl-cl rs'>Coverage (%)</div><div class='bl-cl rs'>Covered/Total Smts.</div>"+(u?"<div class='bl-cl rs'>Covered/Total Branches</div>":"")+"<div style='clear:both;'></div></div>",f="<div class='blanket {{statusclass}}'><div class='bl-cl bl-file'><span class='bl-nb'>{{fileNumber}}.</span><a href='javascript:blanket_toggleSource(\"file-{{fileNumber}}\")'>{{file}}</a></div><div class='bl-cl rs'>{{percentage}} %</div><div class='bl-cl rs'>{{numberCovered}}/{{totalSmts}}</div>"+(u?"<div class='bl-cl rs'>{{passedBranches}}/{{totalBranches}}</div>":"")+"<div id='file-{{fileNumber}}' class='bl-source' style='display:none;'>{{source}}</div><div style='clear:both;'></div></div>";grandTotalTemplate="<div class='blanket grand-total {{statusclass}}'><div class='bl-cl'>{{rowTitle}}</div><div class='bl-cl rs'>{{percentage}} %</div><div class='bl-cl rs'>{{numberCovered}}/{{totalSmts}}</div>"+(u?"<div class='bl-cl rs'>{{passedBranches}}/{{totalBranches}}</div>":"")+"<div style='clear:both;'></div></div>";var c=document.createElement("script");c.type="text/javascript",c.text=l.toString().replace("function "+l.name,"function blanket_toggleSource"),s.appendChild(c);var h=function(e,t){return Math.round(e/t*100*100)/100},p=function(e,t,n){var r=document.createElement(e);r.innerHTML=n,t.appendChild(r)},m=[],y=function(e){return typeof e!="undefined"},b=e.files,w={totalSmts:0,numberOfFilesCovered:0,passedBranches:0,totalBranches:0,moduleTotalStatements:{},moduleTotalCoveredStatements:{},moduleTotalBranches:{},moduleTotalCoveredBranches:{}},E=_blanket.options("modulePattern"),S=E?new RegExp(E):null;for(var x in b){i++;var T=b[x],N=0,C=0,k=[],L,A=[];for(L=0;L<T.source.length;L+=1){var O=T.source[L];if(m.length>0||typeof T.branchData!="undefined")if(typeof T.branchData[L+1]!="undefined"){var M=T.branchData[L+1].filter(y),_=0;O=g(_,O,M,0,L+1).src}else m.length?O=g(0,O,null,0,L+1).src:O=d(O);else O=d(O);var D="";T[L+1]?(C+=1,N+=1,D="hit"):T[L+1]===0&&(N++,D="miss"),k[L+1]="<div class='"+D+"'><span class=''>"+(L+1)+"</span>"+O+"</div>"}w.totalSmts+=N,w.numberOfFilesCovered+=C;var P=0,H=0;if(typeof T.branchData!="undefined")for(var B=0;B<T.branchData.length;B++)if(typeof T.branchData[B]!="undefined")for(var j=0;j<T.branchData[B].length;j++)typeof T.branchData[B][j]!="undefined"&&(P++,typeof T.branchData[B][j][0]!="undefined"&&T.branchData[B][j][0].length>0&&typeof T.branchData[B][j][1]!="undefined"&&T.branchData[B][j][1].length>0&&H++);w.passedBranches+=H,w.totalBranches+=P;if(S){var F=x.match(S)[1];w.moduleTotalStatements.hasOwnProperty(F)||(w.moduleTotalStatements[F]=0,w.moduleTotalCoveredStatements[F]=0),w.moduleTotalStatements[F]+=N,w.moduleTotalCoveredStatements[F]+=C,w.moduleTotalBranches.hasOwnProperty(F)||(w.moduleTotalBranches[F]=0,w.moduleTotalCoveredBranches[F]=0),w.moduleTotalBranches[F]+=P,w.moduleTotalCoveredBranches[F]+=H}var I=h(C,N),q=f.replace("{{file}}",x).replace("{{percentage}}",I).replace("{{numberCovered}}",C).replace(/\{\{fileNumber\}\}/g,i).replace("{{totalSmts}}",N).replace("{{totalBranches}}",P).replace("{{passedBranches}}",H).replace("{{source}}",k.join(" "));I<n?q=q.replace("{{statusclass}}","bl-error"):q=q.replace("{{statusclass}}","bl-success"),a+=q}var R=function(e,t,r,i,s){var o=h(t,e),u=o<n?"bl-error":"bl-success",f=s?"Total for module: "+s:"Global total",l=grandTotalTemplate.replace("{{rowTitle}}",f).replace("{{percentage}}",o).replace("{{numberCovered}}",t).replace("{{totalSmts}}",e).replace("{{passedBranches}}",i).replace("{{totalBranches}}",r).replace("{{statusclass}}",u);a+=l};if(S)for(var U in w.moduleTotalStatements)if(w.moduleTotalStatements.hasOwnProperty(U)){var z=w.moduleTotalStatements[U],W=w.moduleTotalCoveredStatements[U],X=w.moduleTotalBranches[U],V=w.moduleTotalCoveredBranches[U];R(z,W,X,V,U)}R(w.totalSmts,w.numberOfFilesCovered,w.totalBranches,w.passedBranches,null),a+="</div>",p("style",r,t),document.getElementById("blanket-main")?document.getElementById("blanket-main").innerHTML=a.slice(23,-6):p("div",s,a)},function(){var e={},t=Array.prototype.slice,n=t.call(document.scripts);t.call(n[n.length-1].attributes).forEach(function(t){t.nodeName==="data-cover-only"&&(e.filter=t.nodeValue),t.nodeName==="data-cover-never"&&(e.antifilter=t.nodeValue),t.nodeName==="data-cover-reporter"&&(e.reporter=t.nodeValue),t.nodeName==="data-cover-adapter"&&(e.adapter=t.nodeValue),t.nodeName==="data-cover-loader"&&(e.loader=t.nodeValue),t.nodeName==="data-cover-timeout"&&(e.timeout=t.nodeValue),t.nodeName==="data-cover-modulepattern"&&(e.modulePattern=t.nodeValue);if(t.nodeName==="data-cover-reporter-options")try{e.reporter_options=JSON.parse(t.nodeValue)}catch(n){if(blanket.options("debug"))throw new Error("Invalid reporter options.  Must be a valid stringified JSON object.")}t.nodeName==="data-cover-testReadyCallback"&&(e.testReadyCallback=t.nodeValue),t.nodeName==="data-cover-customVariable"&&(e.customVariable=t.nodeValue);if(t.nodeName==="data-cover-flags"){var r=" "+t.nodeValue+" ";r.indexOf(" ignoreError ")>-1&&(e.ignoreScriptError=!0),r.indexOf(" autoStart ")>-1&&(e.autoStart=!0),r.indexOf(" ignoreCors ")>-1&&(e.ignoreCors=!0),r.indexOf(" branchTracking ")>-1&&(e.branchTracking=!0),r.indexOf(" sourceURL ")>-1&&(e.sourceURL=!0),r.indexOf(" debug ")>-1&&(e.debug=!0),r.indexOf(" engineOnly ")>-1&&(e.engineOnly=!0),r.indexOf(" commonJS ")>-1&&(e.commonJS=!0),r.indexOf(" instrumentCache ")>-1&&(e.instrumentCache=!0)}}),blanket.options(e),typeof requirejs!="undefined"&&blanket.options("existingRequireJS",!0),blanket.options("commonJS")&&(blanket._commonjs={})}(),function(e){e.extend({utils:{normalizeBackslashes:function(e){return e.replace(/\\/g,"/")},matchPatternAttribute:function(t,n){if(typeof n=="string"){if(n.indexOf("[")===0){var r=n.slice(1,n.length-1).split(",");return r.some(function(n){return e.utils.matchPatternAttribute(t,e.utils.normalizeBackslashes(n.slice(1,-1)))})}if(n.indexOf("//")===0){var i=n.slice(2,n.lastIndexOf("/")),s=n.slice(n.lastIndexOf("/")+1),o=new RegExp(i,s);return o.test(t)}return n.indexOf("#")===0?window[n.slice(1)].call(window,t):t.indexOf(e.utils.normalizeBackslashes(n))>-1}if(n instanceof Array)return n.some(function(n){return e.utils.matchPatternAttribute(t,n)});if(n instanceof RegExp)return n.test(t);if(typeof n=="function")return n.call(window,t)},blanketEval:function(t){e._addScript(t)},collectPageScripts:function(){var t=Array.prototype.slice,n=t.call(document.scripts),r=[],i=[],s=e.options("filter");if(s!=null){var o=e.options("antifilter");r=t.call(document.scripts).filter(function(n){return t.call(n.attributes).filter(function(t){return t.nodeName==="src"&&e.utils.matchPatternAttribute(t.nodeValue,s)&&(typeof o=="undefined"||!e.utils.matchPatternAttribute(t.nodeValue,o))}).length===1})}else r=t.call(document.querySelectorAll("script[data-cover]"));return i=r.map(function(n){return e.utils.qualifyURL(t.call(n.attributes).filter(function(e){return e.nodeName==="src"})[0].nodeValue)}),s||e.options("filter","['"+i.join("','")+"']"),i},loadAll:function(t,n,r){var i=t(),s=e.utils.scriptIsLoaded(i,e.utils.ifOrdered,t,n);if(!e.utils.cache[i]||!e.utils.cache[i].loaded){var o=function(){e.options("debug")&&console.log("BLANKET-Mark script:"+i+", as loaded and move to next script."),s()},u=function(t){e.options("debug")&&console.log("BLANKET-File loading finished"),typeof t!="undefined"&&(e.options("debug")&&console.log("BLANKET-Add file to DOM."),e._addScript(t)),o()};e.utils.attachScript({url:i},function(t){e.utils.processFile(t,i,u,u)})}else s()},attachScript:function(t,n){var r=e.options("timeout")||3e3;setTimeout(function(){if(!e.utils.cache[t.url].loaded)throw new Error("error loading source script")},r),e.utils.getFile(t.url,n,function(){throw new Error("error loading source script")})},ifOrdered:function(t,n){var r=t(!0);r?e.utils.loadAll(t,n):n(new Error("Error in loading chain."))},scriptIsLoaded:function(t,n,r,i){return e.options("debug")&&console.log("BLANKET-Returning function"),function(){e.options("debug")&&console.log("BLANKET-Marking file as loaded: "+t),e.utils.cache[t].loaded=!0,e.utils.allLoaded()?(e.options("debug")&&console.log("BLANKET-All files loaded"),i()):n&&(e.options("debug")&&console.log("BLANKET-Load next file."),n(r,i))}},cache:{},allLoaded:function(){var t=Object.keys(e.utils.cache);for(var n=0;n<t.length;n++)if(!e.utils.cache[t[n]].loaded)return!1;return!0},processFile:function(t,n,r,i){var s=e.options("filter"),o=e.options("antifilter");typeof o!="undefined"&&e.utils.matchPatternAttribute(n,o)?(i(t),e.options("debug")&&console.log("BLANKET-File will never be instrumented:"+n),e.requiringFile(n,!0)):e.utils.matchPatternAttribute(n,s)?(e.options("debug")&&console.log("BLANKET-Attempting instrument of:"+n),e.instrument({inputFile:t,inputFileName:n},function(i){try{e.options("debug")&&console.log("BLANKET-instrument of:"+n+" was successfull."),e.utils.blanketEval(i),r(),e.requiringFile(n,!0)}catch(s){if(!e.options("ignoreScriptError"))throw new Error("Error parsing instrumented code: "+s);e.options("debug")&&console.log("BLANKET-There was an error loading the file:"+n),r(t),e.requiringFile(n,!0)}})):(e.options("debug")&&console.log("BLANKET-Loading (without instrumenting) the file:"+n),i(t),e.requiringFile(n,!0))},cacheXhrConstructor:function(){var e,t,n,r;if(typeof XMLHttpRequest!="undefined")e=XMLHttpRequest,this.createXhr=function(){return new e};else if(typeof ActiveXObject!="undefined"){e=ActiveXObject;for(n=0;n<3;n+=1){r=progIds[n];try{new ActiveXObject(r);break}catch(i){}}this.createXhr=function(){return new e(r)}}},craeteXhr:function(){throw new Error("cacheXhrConstructor is supposed to overwrite this function.")},getFile:function(t,n,r,i){var s=!1;if(e.blanketSession){var o=Object.keys(e.blanketSession);for(var u=0;u<o.length;u++){var a=o[u];if(t.indexOf(a)>-1){n(e.blanketSession[a]),s=!0;return}}}if(!s){var f=e.utils.createXhr();f.open("GET",t,!0),i&&i(f,t),f.onreadystatechange=function(e){var i,s;f.readyState===4&&(i=f.status,i>399&&i<600?(s=new Error(t+" HTTP status: "+i),s.xhr=f,r(s)):n(f.responseText))};try{f.send(null)}catch(l){if(!l.code||l.code!==101&&l.code!==1012||e.options("ignoreCors")!==!1)throw l;e.showManualLoader()}}}}}),function(){var t=blanket.options("commonJS")?blanket._commonjs.require:window.require,n=blanket.options("commonJS")?blanket._commonjs.requirejs:window.requirejs;!e.options("engineOnly")&&e.options("existingRequireJS")&&(e.utils.oldloader=n.load,n.load=function(t,n,r){e.requiringFile(r),e.utils.getFile(r,function(i){e.utils.processFile(i,r,function(){t.completeLoad(n)},function(){e.utils.oldloader(t,n,r)})},function(t){throw e.requiringFile(),t})}),e.utils.cacheXhrConstructor()}()}(blanket),function(){if(!mocha)throw new Exception("mocha library does not exist in global namespace!");var e=mocha._reporter,t=function(t){t.on("start",function(){blanket.setupCoverage()}),t.on("end",function(){blanket.onTestsDone()}),t.on("suite",function(){blanket.onModuleStart()}),t.on("test",function(){blanket.onTestStart()}),t.on("test end",function(e){blanket.onTestDone(e.parent.tests.length,e.state==="passed")}),e.apply(this,arguments)};mocha.reporter(t);var n=mocha.run,r=null;mocha.run=function(e){r=e,console.log("waiting for blanket...")},blanket.beforeStartTestRunner({callback:function(){blanket.options("existingRequireJS")||n(r),mocha.run=n}})}();
diff --git a/tests/resources/js/json2.js b/tests/resources/js/json2.js
new file mode 100755
index 0000000..c7745df
--- /dev/null
+++ b/tests/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 '&nbsp;'),
+                        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');
+        };
+    }
+}());
diff --git a/tests/resources/js/mocha.js b/tests/resources/js/mocha.js
new file mode 100644
index 0000000..00dcda1
--- /dev/null
+++ b/tests/resources/js/mocha.js
@@ -0,0 +1,5341 @@
+;(function(){
+
+
+// CommonJS require()
+
+function require(p){
+    var path = require.resolve(p)
+      , mod = require.modules[path];
+    if (!mod) throw new Error('failed to require "' + p + '"');
+    if (!mod.exports) {
+      mod.exports = {};
+      mod.call(mod.exports, mod, mod.exports, require.relative(path));
+    }
+    return mod.exports;
+  }
+
+require.modules = {};
+
+require.resolve = function (path){
+    var orig = path
+      , reg = path + '.js'
+      , index = path + '/index.js';
+    return require.modules[reg] && reg
+      || require.modules[index] && index
+      || orig;
+  };
+
+require.register = function (path, fn){
+    require.modules[path] = fn;
+  };
+
+require.relative = function (parent) {
+    return function(p){
+      if ('.' != p.charAt(0)) return require(p);
+      
+      var path = parent.split('/')
+        , segs = p.split('/');
+      path.pop();
+      
+      for (var i = 0; i < segs.length; i++) {
+        var seg = segs[i];
+        if ('..' == seg) path.pop();
+        else if ('.' != seg) path.push(seg);
+      }
+
+      return require(path.join('/'));
+    };
+  };
+
+
+require.register("browser/debug.js", function(module, exports, require){
+
+module.exports = function(type){
+  return function(){
+    
+  }
+};
+}); // module: browser/debug.js
+
+require.register("browser/diff.js", function(module, exports, require){
+/* See license.txt for terms of usage */
+
+/*
+ * Text diff implementation.
+ * 
+ * This library supports the following APIS:
+ * JsDiff.diffChars: Character by character diff
+ * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace
+ * JsDiff.diffLines: Line based diff
+ * 
+ * JsDiff.diffCss: Diff targeted at CSS content
+ * 
+ * These methods are based on the implementation proposed in
+ * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986).
+ * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927
+ */
+var JsDiff = (function() {
+  function clonePath(path) {
+    return { newPos: path.newPos, components: path.components.slice(0) };
+  }
+  function removeEmpty(array) {
+    var ret = [];
+    for (var i = 0; i < array.length; i++) {
+      if (array[i]) {
+        ret.push(array[i]);
+      }
+    }
+    return ret;
+  }
+  function escapeHTML(s) {
+    var n = s;
+    n = n.replace(/&/g, "&amp;");
+    n = n.replace(/</g, "&lt;");
+    n = n.replace(/>/g, "&gt;");
+    n = n.replace(/"/g, "&quot;");
+
+    return n;
+  }
+
+
+  var fbDiff = function(ignoreWhitespace) {
+    this.ignoreWhitespace = ignoreWhitespace;
+  };
+  fbDiff.prototype = {
+      diff: function(oldString, newString) {
+        // Handle the identity case (this is due to unrolling editLength == 0
+        if (newString == oldString) {
+          return [{ value: newString }];
+        }
+        if (!newString) {
+          return [{ value: oldString, removed: true }];
+        }
+        if (!oldString) {
+          return [{ value: newString, added: true }];
+        }
+
+        newString = this.tokenize(newString);
+        oldString = this.tokenize(oldString);
+
+        var newLen = newString.length, oldLen = oldString.length;
+        var maxEditLength = newLen + oldLen;
+        var bestPath = [{ newPos: -1, components: [] }];
+
+        // Seed editLength = 0
+        var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
+        if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) {
+          return bestPath[0].components;
+        }
+
+        for (var editLength = 1; editLength <= maxEditLength; editLength++) {
+          for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) {
+            var basePath;
+            var addPath = bestPath[diagonalPath-1],
+                removePath = bestPath[diagonalPath+1];
+            oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
+            if (addPath) {
+              // No one else is going to attempt to use this value, clear it
+              bestPath[diagonalPath-1] = undefined;
+            }
+
+            var canAdd = addPath && addPath.newPos+1 < newLen;
+            var canRemove = removePath && 0 <= oldPos && oldPos < oldLen;
+            if (!canAdd && !canRemove) {
+              bestPath[diagonalPath] = undefined;
+              continue;
+            }
+
+            // Select the diagonal that we want to branch from. We select the prior
+            // path whose position in the new string is the farthest from the origin
+            // and does not pass the bounds of the diff graph
+            if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
+              basePath = clonePath(removePath);
+              this.pushComponent(basePath.components, oldString[oldPos], undefined, true);
+            } else {
+              basePath = clonePath(addPath);
+              basePath.newPos++;
+              this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined);
+            }
+
+            var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath);
+
+            if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) {
+              return basePath.components;
+            } else {
+              bestPath[diagonalPath] = basePath;
+            }
+          }
+        }
+      },
+
+      pushComponent: function(components, value, added, removed) {
+        var last = components[components.length-1];
+        if (last && last.added === added && last.removed === removed) {
+          // We need to clone here as the component clone operation is just
+          // as shallow array clone
+          components[components.length-1] =
+            {value: this.join(last.value, value), added: added, removed: removed };
+        } else {
+          components.push({value: value, added: added, removed: removed });
+        }
+      },
+      extractCommon: function(basePath, newString, oldString, diagonalPath) {
+        var newLen = newString.length,
+            oldLen = oldString.length,
+            newPos = basePath.newPos,
+            oldPos = newPos - diagonalPath;
+        while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) {
+          newPos++;
+          oldPos++;
+          
+          this.pushComponent(basePath.components, newString[newPos], undefined, undefined);
+        }
+        basePath.newPos = newPos;
+        return oldPos;
+      },
+
+      equals: function(left, right) {
+        var reWhitespace = /\S/;
+        if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) {
+          return true;
+        } else {
+          return left == right;
+        }
+      },
+      join: function(left, right) {
+        return left + right;
+      },
+      tokenize: function(value) {
+        return value;
+      }
+  };
+  
+  var CharDiff = new fbDiff();
+  
+  var WordDiff = new fbDiff(true);
+  WordDiff.tokenize = function(value) {
+    return removeEmpty(value.split(/(\s+|\b)/));
+  };
+  
+  var CssDiff = new fbDiff(true);
+  CssDiff.tokenize = function(value) {
+    return removeEmpty(value.split(/([{}:;,]|\s+)/));
+  };
+  
+  var LineDiff = new fbDiff();
+  LineDiff.tokenize = function(value) {
+    return value.split(/^/m);
+  };
+  
+  return {
+    diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); },
+    diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); },
+    diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); },
+
+    diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); },
+
+    createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) {
+      var ret = [];
+
+      ret.push("Index: " + fileName);
+      ret.push("===================================================================");
+      ret.push("--- " + fileName + (typeof oldHeader === "undefined" ? "" : "\t" + oldHeader));
+      ret.push("+++ " + fileName + (typeof newHeader === "undefined" ? "" : "\t" + newHeader));
+
+      var diff = LineDiff.diff(oldStr, newStr);
+      if (!diff[diff.length-1].value) {
+        diff.pop();   // Remove trailing newline add
+      }
+      diff.push({value: "", lines: []});   // Append an empty value to make cleanup easier
+
+      function contextLines(lines) {
+        return lines.map(function(entry) { return ' ' + entry; });
+      }
+      function eofNL(curRange, i, current) {
+        var last = diff[diff.length-2],
+            isLast = i === diff.length-2,
+            isLastOfType = i === diff.length-3 && (current.added === !last.added || current.removed === !last.removed);
+
+        // Figure out if this is the last line for the given file and missing NL
+        if (!/\n$/.test(current.value) && (isLast || isLastOfType)) {
+          curRange.push('\\ No newline at end of file');
+        }
+      }
+
+      var oldRangeStart = 0, newRangeStart = 0, curRange = [],
+          oldLine = 1, newLine = 1;
+      for (var i = 0; i < diff.length; i++) {
+        var current = diff[i],
+            lines = current.lines || current.value.replace(/\n$/, "").split("\n");
+        current.lines = lines;
+
+        if (current.added || current.removed) {
+          if (!oldRangeStart) {
+            var prev = diff[i-1];
+            oldRangeStart = oldLine;
+            newRangeStart = newLine;
+            
+            if (prev) {
+              curRange = contextLines(prev.lines.slice(-4));
+              oldRangeStart -= curRange.length;
+              newRangeStart -= curRange.length;
+            }
+          }
+          curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?"+":"-") + entry; }));
+          eofNL(curRange, i, current);
+
+          if (current.added) {
+            newLine += lines.length;
+          } else {
+            oldLine += lines.length;
+          }
+        } else {
+          if (oldRangeStart) {
+            // Close out any changes that have been output (or join overlapping)
+            if (lines.length <= 8 && i < diff.length-2) {
+              // Overlapping
+              curRange.push.apply(curRange, contextLines(lines));
+            } else {
+              // end the range and output
+              var contextSize = Math.min(lines.length, 4);
+              ret.push(
+                  "@@ -" + oldRangeStart + "," + (oldLine-oldRangeStart+contextSize)
+                  + " +" + newRangeStart + "," + (newLine-newRangeStart+contextSize)
+                  + " @@");
+              ret.push.apply(ret, curRange);
+              ret.push.apply(ret, contextLines(lines.slice(0, contextSize)));
+              if (lines.length <= 4) {
+                eofNL(ret, i, current);
+              }
+
+              oldRangeStart = 0;  newRangeStart = 0; curRange = [];
+            }
+          }
+          oldLine += lines.length;
+          newLine += lines.length;
+        }
+      }
+
+      return ret.join('\n') + '\n';
+    },
+
+    convertChangesToXML: function(changes){
+      var ret = [];
+      for ( var i = 0; i < changes.length; i++) {
+        var change = changes[i];
+        if (change.added) {
+          ret.push("<ins>");
+        } else if (change.removed) {
+          ret.push("<del>");
+        }
+
+        ret.push(escapeHTML(change.value));
+
+        if (change.added) {
+          ret.push("</ins>");
+        } else if (change.removed) {
+          ret.push("</del>");
+        }
+      }
+      return ret.join("");
+    }
+  };
+})();
+
+if (typeof module !== "undefined") {
+    module.exports = JsDiff;
+}
+
+}); // module: browser/diff.js
+
+require.register("browser/events.js", function(module, exports, require){
+
+/**
+ * Module exports.
+ */
+
+exports.EventEmitter = EventEmitter;
+
+/**
+ * Check if `obj` is an array.
+ */
+
+function isArray(obj) {
+  return '[object Array]' == {}.toString.call(obj);
+}
+
+/**
+ * Event emitter constructor.
+ *
+ * @api public
+ */
+
+function EventEmitter(){};
+
+/**
+ * Adds a listener.
+ *
+ * @api public
+ */
+
+EventEmitter.prototype.on = function (name, fn) {
+  if (!this.$events) {
+    this.$events = {};
+  }
+
+  if (!this.$events[name]) {
+    this.$events[name] = fn;
+  } else if (isArray(this.$events[name])) {
+    this.$events[name].push(fn);
+  } else {
+    this.$events[name] = [this.$events[name], fn];
+  }
+
+  return this;
+};
+
+EventEmitter.prototype.addListener = EventEmitter.prototype.on;
+
+/**
+ * Adds a volatile listener.
+ *
+ * @api public
+ */
+
+EventEmitter.prototype.once = function (name, fn) {
+  var self = this;
+
+  function on () {
+    self.removeListener(name, on);
+    fn.apply(this, arguments);
+  };
+
+  on.listener = fn;
+  this.on(name, on);
+
+  return this;
+};
+
+/**
+ * Removes a listener.
+ *
+ * @api public
+ */
+
+EventEmitter.prototype.removeListener = function (name, fn) {
+  if (this.$events && this.$events[name]) {
+    var list = this.$events[name];
+
+    if (isArray(list)) {
+      var pos = -1;
+
+      for (var i = 0, l = list.length; i < l; i++) {
+        if (list[i] === fn || (list[i].listener && list[i].listener === fn)) {
+          pos = i;
+          break;
+        }
+      }
+
+      if (pos < 0) {
+        return this;
+      }
+
+      list.splice(pos, 1);
+
+      if (!list.length) {
+        delete this.$events[name];
+      }
+    } else if (list === fn || (list.listener && list.listener === fn)) {
+      delete this.$events[name];
+    }
+  }
+
+  return this;
+};
+
+/**
+ * Removes all listeners for an event.
+ *
+ * @api public
+ */
+
+EventEmitter.prototype.removeAllListeners = function (name) {
+  if (name === undefined) {
+    this.$events = {};
+    return this;
+  }
+
+  if (this.$events && this.$events[name]) {
+    this.$events[name] = null;
+  }
+
+  return this;
+};
+
+/**
+ * Gets all listeners for a certain event.
+ *
+ * @api public
+ */
+
+EventEmitter.prototype.listeners = function (name) {
+  if (!this.$events) {
+    this.$events = {};
+  }
+
+  if (!this.$events[name]) {
+    this.$events[name] = [];
+  }
+
+  if (!isArray(this.$events[name])) {
+    this.$events[name] = [this.$events[name]];
+  }
+
+  return this.$events[name];
+};
+
+/**
+ * Emits an event.
+ *
+ * @api public
+ */
+
+EventEmitter.prototype.emit = function (name) {
+  if (!this.$events) {
+    return false;
+  }
+
+  var handler = this.$events[name];
+
+  if (!handler) {
+    return false;
+  }
+
+  var args = [].slice.call(arguments, 1);
+
+  if ('function' == typeof handler) {
+    handler.apply(this, args);
+  } else if (isArray(handler)) {
+    var listeners = handler.slice();
+
+    for (var i = 0, l = listeners.length; i < l; i++) {
+      listeners[i].apply(this, args);
+    }
+  } else {
+    return false;
+  }
+
+  return true;
+};
+}); // module: browser/events.js
+
+require.register("browser/fs.js", function(module, exports, require){
+
+}); // module: browser/fs.js
+
+require.register("browser/path.js", function(module, exports, require){
+
+}); // module: browser/path.js
+
+require.register("browser/progress.js", function(module, exports, require){
+
+/**
+ * Expose `Progress`.
+ */
+
+module.exports = Progress;
+
+/**
+ * Initialize a new `Progress` indicator.
+ */
+
+function Progress() {
+  this.percent = 0;
+  this.size(0);
+  this.fontSize(11);
+  this.font('helvetica, arial, sans-serif');
+}
+
+/**
+ * Set progress size to `n`.
+ *
+ * @param {Number} n
+ * @return {Progress} for chaining
+ * @api public
+ */
+
+Progress.prototype.size = function(n){
+  this._size = n;
+  return this;
+};
+
+/**
+ * Set text to `str`.
+ *
+ * @param {String} str
+ * @return {Progress} for chaining
+ * @api public
+ */
+
+Progress.prototype.text = function(str){
+  this._text = str;
+  return this;
+};
+
+/**
+ * Set font size to `n`.
+ *
+ * @param {Number} n
+ * @return {Progress} for chaining
+ * @api public
+ */
+
+Progress.prototype.fontSize = function(n){
+  this._fontSize = n;
+  return this;
+};
+
+/**
+ * Set font `family`.
+ *
+ * @param {String} family
+ * @return {Progress} for chaining
+ */
+
+Progress.prototype.font = function(family){
+  this._font = family;
+  return this;
+};
+
+/**
+ * Update percentage to `n`.
+ *
+ * @param {Number} n
+ * @return {Progress} for chaining
+ */
+
+Progress.prototype.update = function(n){
+  this.percent = n;
+  return this;
+};
+
+/**
+ * Draw on `ctx`.
+ *
+ * @param {CanvasRenderingContext2d} ctx
+ * @return {Progress} for chaining
+ */
+
+Progress.prototype.draw = function(ctx){
+  var percent = Math.min(this.percent, 100)
+    , size = this._size
+    , half = size / 2
+    , x = half
+    , y = half
+    , rad = half - 1
+    , fontSize = this._fontSize;
+
+  ctx.font = fontSize + 'px ' + this._font;
+
+  var angle = Math.PI * 2 * (percent / 100);
+  ctx.clearRect(0, 0, size, size);
+
+  // outer circle
+  ctx.strokeStyle = '#9f9f9f';
+  ctx.beginPath();
+  ctx.arc(x, y, rad, 0, angle, false);
+  ctx.stroke();
+
+  // inner circle
+  ctx.strokeStyle = '#eee';
+  ctx.beginPath();
+  ctx.arc(x, y, rad - 1, 0, angle, true);
+  ctx.stroke();
+
+  // text
+  var text = this._text || (percent | 0) + '%'
+    , w = ctx.measureText(text).width;
+
+  ctx.fillText(
+      text
+    , x - w / 2 + 1
+    , y + fontSize / 2 - 1);
+
+  return this;
+};
+
+}); // module: browser/progress.js
+
+require.register("browser/tty.js", function(module, exports, require){
+
+exports.isatty = function(){
+  return true;
+};
+
+exports.getWindowSize = function(){
+  return [window.innerHeight, window.innerWidth];
+};
+}); // module: browser/tty.js
+
+require.register("context.js", function(module, exports, require){
+
+/**
+ * Expose `Context`.
+ */
+
+module.exports = Context;
+
+/**
+ * Initialize a new `Context`.
+ *
+ * @api private
+ */
+
+function Context(){}
+
+/**
+ * Set or get the context `Runnable` to `runnable`.
+ *
+ * @param {Runnable} runnable
+ * @return {Context}
+ * @api private
+ */
+
+Context.prototype.runnable = function(runnable){
+  if (0 == arguments.length) return this._runnable;
+  this.test = this._runnable = runnable;
+  return this;
+};
+
+/**
+ * Set test timeout `ms`.
+ *
+ * @param {Number} ms
+ * @return {Context} self
+ * @api private
+ */
+
+Context.prototype.timeout = function(ms){
+  this.runnable().timeout(ms);
+  return this;
+};
+
+/**
+ * Set test slowness threshold `ms`.
+ *
+ * @param {Number} ms
+ * @return {Context} self
+ * @api private
+ */
+
+Context.prototype.slow = function(ms){
+  this.runnable().slow(ms);
+  return this;
+};
+
+/**
+ * Inspect the context void of `._runnable`.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Context.prototype.inspect = function(){
+  return JSON.stringify(this, function(key, val){
+    if ('_runnable' == key) return;
+    if ('test' == key) return;
+    return val;
+  }, 2);
+};
+
+}); // module: context.js
+
+require.register("hook.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Runnable = require('./runnable');
+
+/**
+ * Expose `Hook`.
+ */
+
+module.exports = Hook;
+
+/**
+ * Initialize a new `Hook` with the given `title` and callback `fn`.
+ *
+ * @param {String} title
+ * @param {Function} fn
+ * @api private
+ */
+
+function Hook(title, fn) {
+  Runnable.call(this, title, fn);
+  this.type = 'hook';
+}
+
+/**
+ * Inherit from `Runnable.prototype`.
+ */
+
+function F(){};
+F.prototype = Runnable.prototype;
+Hook.prototype = new F;
+Hook.prototype.constructor = Hook;
+
+
+/**
+ * Get or set the test `err`.
+ *
+ * @param {Error} err
+ * @return {Error}
+ * @api public
+ */
+
+Hook.prototype.error = function(err){
+  if (0 == arguments.length) {
+    var err = this._error;
+    this._error = null;
+    return err;
+  }
+
+  this._error = err;
+};
+
+
+}); // module: hook.js
+
+require.register("interfaces/bdd.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Suite = require('../suite')
+  , Test = require('../test');
+
+/**
+ * BDD-style interface:
+ * 
+ *      describe('Array', function(){
+ *        describe('#indexOf()', function(){
+ *          it('should return -1 when not present', function(){
+ *
+ *          });
+ *
+ *          it('should return the index when present', function(){
+ *
+ *          });
+ *        });
+ *      });
+ * 
+ */
+
+module.exports = function(suite){
+  var suites = [suite];
+
+  suite.on('pre-require', function(context, file, mocha){
+
+    /**
+     * Execute before running tests.
+     */
+
+    context.before = function(fn){
+      suites[0].beforeAll(fn);
+    };
+
+    /**
+     * Execute after running tests.
+     */
+
+    context.after = function(fn){
+      suites[0].afterAll(fn);
+    };
+
+    /**
+     * Execute before each test case.
+     */
+
+    context.beforeEach = function(fn){
+      suites[0].beforeEach(fn);
+    };
+
+    /**
+     * Execute after each test case.
+     */
+
+    context.afterEach = function(fn){
+      suites[0].afterEach(fn);
+    };
+
+    /**
+     * Describe a "suite" with the given `title`
+     * and callback `fn` containing nested suites
+     * and/or tests.
+     */
+  
+    context.describe = context.context = function(title, fn){
+      var suite = Suite.create(suites[0], title);
+      suites.unshift(suite);
+      fn.call(suite);
+      suites.shift();
+      return suite;
+    };
+
+    /**
+     * Pending describe.
+     */
+
+    context.xdescribe =
+    context.xcontext =
+    context.describe.skip = function(title, fn){
+      var suite = Suite.create(suites[0], title);
+      suite.pending = true;
+      suites.unshift(suite);
+      fn.call(suite);
+      suites.shift();
+    };
+
+    /**
+     * Exclusive suite.
+     */
+
+    context.describe.only = function(title, fn){
+      var suite = context.describe(title, fn);
+      mocha.grep(suite.fullTitle());
+    };
+
+    /**
+     * Describe a specification or test-case
+     * with the given `title` and callback `fn`
+     * acting as a thunk.
+     */
+
+    context.it = context.specify = function(title, fn){
+      var suite = suites[0];
+      if (suite.pending) var fn = null;
+      var test = new Test(title, fn);
+      suite.addTest(test);
+      return test;
+    };
+
+    /**
+     * Exclusive test-case.
+     */
+
+    context.it.only = function(title, fn){
+      var test = context.it(title, fn);
+      mocha.grep(test.fullTitle());
+    };
+
+    /**
+     * Pending test case.
+     */
+
+    context.xit =
+    context.xspecify =
+    context.it.skip = function(title){
+      context.it(title);
+    };
+  });
+};
+
+}); // module: interfaces/bdd.js
+
+require.register("interfaces/exports.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Suite = require('../suite')
+  , Test = require('../test');
+
+/**
+ * TDD-style interface:
+ * 
+ *     exports.Array = {
+ *       '#indexOf()': {
+ *         'should return -1 when the value is not present': function(){
+ *           
+ *         },
+ *
+ *         'should return the correct index when the value is present': function(){
+ *           
+ *         }
+ *       }
+ *     };
+ * 
+ */
+
+module.exports = function(suite){
+  var suites = [suite];
+
+  suite.on('require', visit);
+
+  function visit(obj) {
+    var suite;
+    for (var key in obj) {
+      if ('function' == typeof obj[key]) {
+        var fn = obj[key];
+        switch (key) {
+          case 'before':
+            suites[0].beforeAll(fn);
+            break;
+          case 'after':
+            suites[0].afterAll(fn);
+            break;
+          case 'beforeEach':
+            suites[0].beforeEach(fn);
+            break;
+          case 'afterEach':
+            suites[0].afterEach(fn);
+            break;
+          default:
+            suites[0].addTest(new Test(key, fn));
+        }
+      } else {
+        var suite = Suite.create(suites[0], key);
+        suites.unshift(suite);
+        visit(obj[key]);
+        suites.shift();
+      }
+    }
+  }
+};
+}); // module: interfaces/exports.js
+
+require.register("interfaces/index.js", function(module, exports, require){
+
+exports.bdd = require('./bdd');
+exports.tdd = require('./tdd');
+exports.qunit = require('./qunit');
+exports.exports = require('./exports');
+
+}); // module: interfaces/index.js
+
+require.register("interfaces/qunit.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Suite = require('../suite')
+  , Test = require('../test');
+
+/**
+ * QUnit-style interface:
+ * 
+ *     suite('Array');
+ *     
+ *     test('#length', function(){
+ *       var arr = [1,2,3];
+ *       ok(arr.length == 3);
+ *     });
+ *     
+ *     test('#indexOf()', function(){
+ *       var arr = [1,2,3];
+ *       ok(arr.indexOf(1) == 0);
+ *       ok(arr.indexOf(2) == 1);
+ *       ok(arr.indexOf(3) == 2);
+ *     });
+ *     
+ *     suite('String');
+ *     
+ *     test('#length', function(){
+ *       ok('foo'.length == 3);
+ *     });
+ * 
+ */
+
+module.exports = function(suite){
+  var suites = [suite];
+
+  suite.on('pre-require', function(context){
+
+    /**
+     * Execute before running tests.
+     */
+
+    context.before = function(fn){
+      suites[0].beforeAll(fn);
+    };
+
+    /**
+     * Execute after running tests.
+     */
+
+    context.after = function(fn){
+      suites[0].afterAll(fn);
+    };
+
+    /**
+     * Execute before each test case.
+     */
+
+    context.beforeEach = function(fn){
+      suites[0].beforeEach(fn);
+    };
+
+    /**
+     * Execute after each test case.
+     */
+
+    context.afterEach = function(fn){
+      suites[0].afterEach(fn);
+    };
+
+    /**
+     * Describe a "suite" with the given `title`.
+     */
+  
+    context.suite = function(title){
+      if (suites.length > 1) suites.shift();
+      var suite = Suite.create(suites[0], title);
+      suites.unshift(suite);
+    };
+
+    /**
+     * Describe a specification or test-case
+     * with the given `title` and callback `fn`
+     * acting as a thunk.
+     */
+
+    context.test = function(title, fn){
+      suites[0].addTest(new Test(title, fn));
+    };
+  });
+};
+
+}); // module: interfaces/qunit.js
+
+require.register("interfaces/tdd.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Suite = require('../suite')
+  , Test = require('../test');
+
+/**
+ * TDD-style interface:
+ *
+ *      suite('Array', function(){
+ *        suite('#indexOf()', function(){
+ *          suiteSetup(function(){
+ *
+ *          });
+ *          
+ *          test('should return -1 when not present', function(){
+ *
+ *          });
+ *
+ *          test('should return the index when present', function(){
+ *
+ *          });
+ *
+ *          suiteTeardown(function(){
+ *
+ *          });
+ *        });
+ *      });
+ *
+ */
+
+module.exports = function(suite){
+  var suites = [suite];
+
+  suite.on('pre-require', function(context, file, mocha){
+
+    /**
+     * Execute before each test case.
+     */
+
+    context.setup = function(fn){
+      suites[0].beforeEach(fn);
+    };
+
+    /**
+     * Execute after each test case.
+     */
+
+    context.teardown = function(fn){
+      suites[0].afterEach(fn);
+    };
+
+    /**
+     * Execute before the suite.
+     */
+
+    context.suiteSetup = function(fn){
+      suites[0].beforeAll(fn);
+    };
+
+    /**
+     * Execute after the suite.
+     */
+
+    context.suiteTeardown = function(fn){
+      suites[0].afterAll(fn);
+    };
+
+    /**
+     * Describe a "suite" with the given `title`
+     * and callback `fn` containing nested suites
+     * and/or tests.
+     */
+
+    context.suite = function(title, fn){
+      var suite = Suite.create(suites[0], title);
+      suites.unshift(suite);
+      fn.call(suite);
+      suites.shift();
+      return suite;
+    };
+
+    /**
+     * Exclusive test-case.
+     */
+
+    context.suite.only = function(title, fn){
+      var suite = context.suite(title, fn);
+      mocha.grep(suite.fullTitle());
+    };
+
+    /**
+     * Describe a specification or test-case
+     * with the given `title` and callback `fn`
+     * acting as a thunk.
+     */
+
+    context.test = function(title, fn){
+      var test = new Test(title, fn);
+      suites[0].addTest(test);
+      return test;
+    };
+
+    /**
+     * Exclusive test-case.
+     */
+
+    context.test.only = function(title, fn){
+      var test = context.test(title, fn);
+      mocha.grep(test.fullTitle());
+    };
+
+    /**
+     * Pending test case.
+     */
+
+    context.test.skip = function(title){
+      context.test(title);
+    };
+  });
+};
+
+}); // module: interfaces/tdd.js
+
+require.register("mocha.js", function(module, exports, require){
+/*!
+ * mocha
+ * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var path = require('browser/path')
+  , utils = require('./utils');
+
+/**
+ * Expose `Mocha`.
+ */
+
+exports = module.exports = Mocha;
+
+/**
+ * Expose internals.
+ */
+
+exports.utils = utils;
+exports.interfaces = require('./interfaces');
+exports.reporters = require('./reporters');
+exports.Runnable = require('./runnable');
+exports.Context = require('./context');
+exports.Runner = require('./runner');
+exports.Suite = require('./suite');
+exports.Hook = require('./hook');
+exports.Test = require('./test');
+
+/**
+ * Return image `name` path.
+ *
+ * @param {String} name
+ * @return {String}
+ * @api private
+ */
+
+function image(name) {
+  return __dirname + '/../images/' + name + '.png';
+}
+
+/**
+ * Setup mocha with `options`.
+ *
+ * Options:
+ *
+ *   - `ui` name "bdd", "tdd", "exports" etc
+ *   - `reporter` reporter instance, defaults to `mocha.reporters.Dot`
+ *   - `globals` array of accepted globals
+ *   - `timeout` timeout in milliseconds
+ *   - `bail` bail on the first test failure
+ *   - `slow` milliseconds to wait before considering a test slow
+ *   - `ignoreLeaks` ignore global leaks
+ *   - `grep` string or regexp to filter tests with
+ *
+ * @param {Object} options
+ * @api public
+ */
+
+function Mocha(options) {
+  options = options || {};
+  this.files = [];
+  this.options = options;
+  this.grep(options.grep);
+  this.suite = new exports.Suite('', new exports.Context);
+  this.ui(options.ui);
+  this.bail(options.bail);
+  this.reporter(options.reporter);
+  if (options.timeout) this.timeout(options.timeout);
+  if (options.slow) this.slow(options.slow);
+}
+
+/**
+ * Enable or disable bailing on the first failure.
+ *
+ * @param {Boolean} [bail]
+ * @api public
+ */
+
+Mocha.prototype.bail = function(bail){
+  if (0 == arguments.length) bail = true;
+  this.suite.bail(bail);
+  return this;
+};
+
+/**
+ * Add test `file`.
+ *
+ * @param {String} file
+ * @api public
+ */
+
+Mocha.prototype.addFile = function(file){
+  this.files.push(file);
+  return this;
+};
+
+/**
+ * Set reporter to `reporter`, defaults to "dot".
+ *
+ * @param {String|Function} reporter name or constructor
+ * @api public
+ */
+
+Mocha.prototype.reporter = function(reporter){
+  if ('function' == typeof reporter) {
+    this._reporter = reporter;
+  } else {
+    reporter = reporter || 'dot';
+    try {
+      this._reporter = require('./reporters/' + reporter);
+    } catch (err) {
+      this._reporter = require(reporter);
+    }
+    if (!this._reporter) throw new Error('invalid reporter "' + reporter + '"');
+  }
+  return this;
+};
+
+/**
+ * Set test UI `name`, defaults to "bdd".
+ *
+ * @param {String} bdd
+ * @api public
+ */
+
+Mocha.prototype.ui = function(name){
+  name = name || 'bdd';
+  this._ui = exports.interfaces[name];
+  if (!this._ui) throw new Error('invalid interface "' + name + '"');
+  this._ui = this._ui(this.suite);
+  return this;
+};
+
+/**
+ * Load registered files.
+ *
+ * @api private
+ */
+
+Mocha.prototype.loadFiles = function(fn){
+  var self = this;
+  var suite = this.suite;
+  var pending = this.files.length;
+  this.files.forEach(function(file){
+    file = path.resolve(file);
+    suite.emit('pre-require', global, file, self);
+    suite.emit('require', require(file), file, self);
+    suite.emit('post-require', global, file, self);
+    --pending || (fn && fn());
+  });
+};
+
+/**
+ * Enable growl support.
+ *
+ * @api private
+ */
+
+Mocha.prototype._growl = function(runner, reporter) {
+  var notify = require('growl');
+
+  runner.on('end', function(){
+    var stats = reporter.stats;
+    if (stats.failures) {
+      var msg = stats.failures + ' of ' + runner.total + ' tests failed';
+      notify(msg, { name: 'mocha', title: 'Failed', image: image('error') });
+    } else {
+      notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', {
+          name: 'mocha'
+        , title: 'Passed'
+        , image: image('ok')
+      });
+    }
+  });
+};
+
+/**
+ * Add regexp to grep, if `re` is a string it is escaped.
+ *
+ * @param {RegExp|String} re
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.grep = function(re){
+  this.options.grep = 'string' == typeof re
+    ? new RegExp(utils.escapeRegexp(re))
+    : re;
+  return this;
+};
+
+/**
+ * Invert `.grep()` matches.
+ *
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.invert = function(){
+  this.options.invert = true;
+  return this;
+};
+
+/**
+ * Ignore global leaks.
+ *
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.ignoreLeaks = function(){
+  this.options.ignoreLeaks = true;
+  return this;
+};
+
+/**
+ * Enable global leak checking.
+ *
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.checkLeaks = function(){
+  this.options.ignoreLeaks = false;
+  return this;
+};
+
+/**
+ * Enable growl support.
+ *
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.growl = function(){
+  this.options.growl = true;
+  return this;
+};
+
+/**
+ * Ignore `globals` array or string.
+ *
+ * @param {Array|String} globals
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.globals = function(globals){
+  this.options.globals = (this.options.globals || []).concat(globals);
+  return this;
+};
+
+/**
+ * Set the timeout in milliseconds.
+ *
+ * @param {Number} timeout
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.timeout = function(timeout){
+  this.suite.timeout(timeout);
+  return this;
+};
+
+/**
+ * Set slowness threshold in milliseconds.
+ *
+ * @param {Number} slow
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.slow = function(slow){
+  this.suite.slow(slow);
+  return this;
+};
+
+/**
+ * Makes all tests async (accepting a callback)
+ *
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.asyncOnly = function(){
+  this.options.asyncOnly = true;
+  return this;
+};
+
+/**
+ * Run tests and invoke `fn()` when complete.
+ *
+ * @param {Function} fn
+ * @return {Runner}
+ * @api public
+ */
+
+Mocha.prototype.run = function(fn){
+  if (this.files.length) this.loadFiles();
+  var suite = this.suite;
+  var options = this.options;
+  var runner = new exports.Runner(suite);
+  var reporter = new this._reporter(runner);
+  runner.ignoreLeaks = options.ignoreLeaks;
+  runner.asyncOnly = options.asyncOnly;
+  if (options.grep) runner.grep(options.grep, options.invert);
+  if (options.globals) runner.globals(options.globals);
+  if (options.growl) this._growl(runner, reporter);
+  return runner.run(fn);
+};
+
+}); // module: blanket_mocha.js
+
+require.register("ms.js", function(module, exports, require){
+
+/**
+ * Helpers.
+ */
+
+var s = 1000;
+var m = s * 60;
+var h = m * 60;
+var d = h * 24;
+
+/**
+ * Parse or format the given `val`.
+ *
+ * @param {String|Number} val
+ * @return {String|Number}
+ * @api public
+ */
+
+module.exports = function(val){
+  if ('string' == typeof val) return parse(val);
+  return format(val);
+}
+
+/**
+ * Parse the given `str` and return milliseconds.
+ *
+ * @param {String} str
+ * @return {Number}
+ * @api private
+ */
+
+function parse(str) {
+  var m = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str);
+  if (!m) return;
+  var n = parseFloat(m[1]);
+  var type = (m[2] || 'ms').toLowerCase();
+  switch (type) {
+    case 'years':
+    case 'year':
+    case 'y':
+      return n * 31557600000;
+    case 'days':
+    case 'day':
+    case 'd':
+      return n * 86400000;
+    case 'hours':
+    case 'hour':
+    case 'h':
+      return n * 3600000;
+    case 'minutes':
+    case 'minute':
+    case 'm':
+      return n * 60000;
+    case 'seconds':
+    case 'second':
+    case 's':
+      return n * 1000;
+    case 'ms':
+      return n;
+  }
+}
+
+/**
+ * Format the given `ms`.
+ *
+ * @param {Number} ms
+ * @return {String}
+ * @api public
+ */
+
+function format(ms) {
+  if (ms == d) return Math.round(ms / d) + ' day';
+  if (ms > d) return Math.round(ms / d) + ' days';
+  if (ms == h) return Math.round(ms / h) + ' hour';
+  if (ms > h) return Math.round(ms / h) + ' hours';
+  if (ms == m) return Math.round(ms / m) + ' minute';
+  if (ms > m) return Math.round(ms / m) + ' minutes';
+  if (ms == s) return Math.round(ms / s) + ' second';
+  if (ms > s) return Math.round(ms / s) + ' seconds';
+  return ms + ' ms';
+}
+}); // module: ms.js
+
+require.register("reporters/base.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var tty = require('browser/tty')
+  , diff = require('browser/diff')
+  , ms = require('../ms');
+
+/**
+ * Save timer references to avoid Sinon interfering (see GH-237).
+ */
+
+var Date = global.Date
+  , setTimeout = global.setTimeout
+  , setInterval = global.setInterval
+  , clearTimeout = global.clearTimeout
+  , clearInterval = global.clearInterval;
+
+/**
+ * Check if both stdio streams are associated with a tty.
+ */
+
+var isatty = tty.isatty(1) && tty.isatty(2);
+
+/**
+ * Expose `Base`.
+ */
+
+exports = module.exports = Base;
+
+/**
+ * Enable coloring by default.
+ */
+
+exports.useColors = isatty;
+
+/**
+ * Default color map.
+ */
+
+exports.colors = {
+    'pass': 90
+  , 'fail': 31
+  , 'bright pass': 92
+  , 'bright fail': 91
+  , 'bright yellow': 93
+  , 'pending': 36
+  , 'suite': 0
+  , 'error title': 0
+  , 'error message': 31
+  , 'error stack': 90
+  , 'checkmark': 32
+  , 'fast': 90
+  , 'medium': 33
+  , 'slow': 31
+  , 'green': 32
+  , 'light': 90
+  , 'diff gutter': 90
+  , 'diff added': 42
+  , 'diff removed': 41
+};
+
+/**
+ * Default symbol map.
+ */
+ 
+exports.symbols = {
+  ok: '✓',
+  err: '✖',
+  dot: '․'
+};
+
+// With node.js on Windows: use symbols available in terminal default fonts
+if ('win32' == process.platform) {
+  exports.symbols.ok = '\u221A';
+  exports.symbols.err = '\u00D7';
+  exports.symbols.dot = '.';
+}
+
+/**
+ * Color `str` with the given `type`,
+ * allowing colors to be disabled,
+ * as well as user-defined color
+ * schemes.
+ *
+ * @param {String} type
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+var color = exports.color = function(type, str) {
+  if (!exports.useColors) return str;
+  return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
+};
+
+/**
+ * Expose term window size, with some
+ * defaults for when stderr is not a tty.
+ */
+
+exports.window = {
+  width: isatty
+    ? process.stdout.getWindowSize
+      ? process.stdout.getWindowSize(1)[0]
+      : tty.getWindowSize()[1]
+    : 75
+};
+
+/**
+ * Expose some basic cursor interactions
+ * that are common among reporters.
+ */
+
+exports.cursor = {
+  hide: function(){
+    process.stdout.write('\u001b[?25l');
+  },
+
+  show: function(){
+    process.stdout.write('\u001b[?25h');
+  },
+
+  deleteLine: function(){
+    process.stdout.write('\u001b[2K');
+  },
+
+  beginningOfLine: function(){
+    process.stdout.write('\u001b[0G');
+  },
+
+  CR: function(){
+    exports.cursor.deleteLine();
+    exports.cursor.beginningOfLine();
+  }
+};
+
+/**
+ * Outut the given `failures` as a list.
+ *
+ * @param {Array} failures
+ * @api public
+ */
+
+exports.list = function(failures){
+  console.error();
+  failures.forEach(function(test, i){
+    // format
+    var fmt = color('error title', '  %s) %s:\n')
+      + color('error message', '     %s')
+      + color('error stack', '\n%s\n');
+
+    // msg
+    var err = test.err
+      , message = err.message || ''
+      , stack = err.stack || message
+      , index = stack.indexOf(message) + message.length
+      , msg = stack.slice(0, index)
+      , actual = err.actual
+      , expected = err.expected
+      , escape = true;
+
+    // explicitly show diff
+    if (err.showDiff) {
+      escape = false;
+      err.actual = actual = JSON.stringify(actual, null, 2);
+      err.expected = expected = JSON.stringify(expected, null, 2);
+    }
+
+    // actual / expected diff
+    if ('string' == typeof actual && 'string' == typeof expected) {
+      var len = Math.max(actual.length, expected.length);
+
+      if (len < 20) msg = errorDiff(err, 'Chars', escape);
+      else msg = errorDiff(err, 'Words', escape);
+
+      // linenos
+      var lines = msg.split('\n');
+      if (lines.length > 4) {
+        var width = String(lines.length).length;
+        msg = lines.map(function(str, i){
+          return pad(++i, width) + ' |' + ' ' + str;
+        }).join('\n');
+      }
+
+      // legend
+      msg = '\n'
+        + color('diff removed', 'actual')
+        + ' '
+        + color('diff added', 'expected')
+        + '\n\n'
+        + msg
+        + '\n';
+
+      // indent
+      msg = msg.replace(/^/gm, '      ');
+
+      fmt = color('error title', '  %s) %s:\n%s')
+        + color('error stack', '\n%s\n');
+    }
+
+    // indent stack trace without msg
+    stack = stack.slice(index ? index + 1 : index)
+      .replace(/^/gm, '  ');
+
+    console.error(fmt, (i + 1), test.fullTitle(), msg, stack);
+  });
+};
+
+/**
+ * Initialize a new `Base` reporter.
+ *
+ * All other reporters generally
+ * inherit from this reporter, providing
+ * stats such as test duration, number
+ * of tests passed / failed etc.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function Base(runner) {
+  var self = this
+    , stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 }
+    , failures = this.failures = [];
+
+  if (!runner) return;
+  this.runner = runner;
+
+  runner.stats = stats;
+
+  runner.on('start', function(){
+    stats.start = new Date;
+  });
+
+  runner.on('suite', function(suite){
+    stats.suites = stats.suites || 0;
+    suite.root || stats.suites++;
+  });
+
+  runner.on('test end', function(test){
+    stats.tests = stats.tests || 0;
+    stats.tests++;
+  });
+
+  runner.on('pass', function(test){
+    stats.passes = stats.passes || 0;
+
+    var medium = test.slow() / 2;
+    test.speed = test.duration > test.slow()
+      ? 'slow'
+      : test.duration > medium
+        ? 'medium'
+        : 'fast';
+
+    stats.passes++;
+  });
+
+  runner.on('fail', function(test, err){
+    stats.failures = stats.failures || 0;
+    stats.failures++;
+    test.err = err;
+    failures.push(test);
+  });
+
+  runner.on('end', function(){
+    stats.end = new Date;
+    stats.duration = new Date - stats.start;
+  });
+
+  runner.on('pending', function(){
+    stats.pending++;
+  });
+}
+
+/**
+ * Output common epilogue used by many of
+ * the bundled reporters.
+ *
+ * @api public
+ */
+
+Base.prototype.epilogue = function(){
+  var stats = this.stats
+    , fmt
+    , tests;
+
+  console.log();
+
+  function pluralize(n) {
+    return 1 == n ? 'test' : 'tests';
+  }
+
+  // failure
+  if (stats.failures) {
+    fmt = color('bright fail', '  ' + exports.symbols.err)
+      + color('fail', ' %d of %d %s failed')
+      + color('light', ':')
+
+    console.error(fmt,
+      stats.failures,
+      this.runner.total,
+      pluralize(this.runner.total));
+
+    Base.list(this.failures);
+    console.error();
+    return;
+  }
+
+  // pass
+  fmt = color('bright pass', ' ')
+    + color('green', ' %d %s complete')
+    + color('light', ' (%s)');
+
+  console.log(fmt,
+    stats.tests || 0,
+    pluralize(stats.tests),
+    ms(stats.duration));
+
+  // pending
+  if (stats.pending) {
+    fmt = color('pending', ' ')
+      + color('pending', ' %d %s pending');
+
+    console.log(fmt, stats.pending, pluralize(stats.pending));
+  }
+
+  console.log();
+};
+
+/**
+ * Pad the given `str` to `len`.
+ *
+ * @param {String} str
+ * @param {String} len
+ * @return {String}
+ * @api private
+ */
+
+function pad(str, len) {
+  str = String(str);
+  return Array(len - str.length + 1).join(' ') + str;
+}
+
+/**
+ * Return a character diff for `err`.
+ *
+ * @param {Error} err
+ * @return {String}
+ * @api private
+ */
+
+function errorDiff(err, type, escape) {
+  return diff['diff' + type](err.actual, err.expected).map(function(str){
+    if (escape) {
+      str.value = str.value
+        .replace(/\t/g, '<tab>')
+        .replace(/\r/g, '<CR>')
+        .replace(/\n/g, '<LF>\n');
+    }
+    if (str.added) return colorLines('diff added', str.value);
+    if (str.removed) return colorLines('diff removed', str.value);
+    return str.value;
+  }).join('');
+}
+
+/**
+ * Color lines for `str`, using the color `name`.
+ *
+ * @param {String} name
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+function colorLines(name, str) {
+  return str.split('\n').map(function(str){
+    return color(name, str);
+  }).join('\n');
+}
+
+}); // module: reporters/base.js
+
+require.register("reporters/doc.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , utils = require('../utils');
+
+/**
+ * Expose `Doc`.
+ */
+
+exports = module.exports = Doc;
+
+/**
+ * Initialize a new `Doc` reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function Doc(runner) {
+  Base.call(this, runner);
+
+  var self = this
+    , stats = this.stats
+    , total = runner.total
+    , indents = 2;
+
+  function indent() {
+    return Array(indents).join('  ');
+  }
+
+  runner.on('suite', function(suite){
+    if (suite.root) return;
+    ++indents;
+    console.log('%s<section class="suite">', indent());
+    ++indents;
+    console.log('%s<h1>%s</h1>', indent(), utils.escape(suite.title));
+    console.log('%s<dl>', indent());
+  });
+
+  runner.on('suite end', function(suite){
+    if (suite.root) return;
+    console.log('%s</dl>', indent());
+    --indents;
+    console.log('%s</section>', indent());
+    --indents;
+  });
+
+  runner.on('pass', function(test){
+    console.log('%s  <dt>%s</dt>', indent(), utils.escape(test.title));
+    var code = utils.escape(utils.clean(test.fn.toString()));
+    console.log('%s  <dd><pre><code>%s</code></pre></dd>', indent(), code);
+  });
+}
+
+}); // module: reporters/doc.js
+
+require.register("reporters/dot.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , color = Base.color;
+
+/**
+ * Expose `Dot`.
+ */
+
+exports = module.exports = Dot;
+
+/**
+ * Initialize a new `Dot` matrix test reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function Dot(runner) {
+  Base.call(this, runner);
+
+  var self = this
+    , stats = this.stats
+    , width = Base.window.width * .75 | 0
+    , n = 0;
+
+  runner.on('start', function(){
+    process.stdout.write('\n  ');
+  });
+
+  runner.on('pending', function(test){
+    process.stdout.write(color('pending', Base.symbols.dot));
+  });
+
+  runner.on('pass', function(test){
+    if (++n % width == 0) process.stdout.write('\n  ');
+    if ('slow' == test.speed) {
+      process.stdout.write(color('bright yellow', Base.symbols.dot));
+    } else {
+      process.stdout.write(color(test.speed, Base.symbols.dot));
+    }
+  });
+
+  runner.on('fail', function(test, err){
+    if (++n % width == 0) process.stdout.write('\n  ');
+    process.stdout.write(color('fail', Base.symbols.dot));
+  });
+
+  runner.on('end', function(){
+    console.log();
+    self.epilogue();
+  });
+}
+
+/**
+ * Inherit from `Base.prototype`.
+ */
+
+function F(){};
+F.prototype = Base.prototype;
+Dot.prototype = new F;
+Dot.prototype.constructor = Dot;
+
+}); // module: reporters/dot.js
+
+require.register("reporters/html-cov.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var JSONCov = require('./json-cov')
+  , fs = require('browser/fs');
+
+/**
+ * Expose `HTMLCov`.
+ */
+
+exports = module.exports = HTMLCov;
+
+/**
+ * Initialize a new `JsCoverage` reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function HTMLCov(runner) {
+  var jade = require('jade')
+    , file = __dirname + '/templates/coverage.jade'
+    , str = fs.readFileSync(file, 'utf8')
+    , fn = jade.compile(str, { filename: file })
+    , self = this;
+
+  JSONCov.call(this, runner, false);
+
+  runner.on('end', function(){
+    process.stdout.write(fn({
+        cov: self.cov
+      , coverageClass: coverageClass
+    }));
+  });
+}
+
+/**
+ * Return coverage class for `n`.
+ *
+ * @return {String}
+ * @api private
+ */
+
+function coverageClass(n) {
+  if (n >= 75) return 'high';
+  if (n >= 50) return 'medium';
+  if (n >= 25) return 'low';
+  return 'terrible';
+}
+}); // module: reporters/html-cov.js
+
+require.register("reporters/html.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , utils = require('../utils')
+  , Progress = require('../browser/progress')
+  , escape = utils.escape;
+
+/**
+ * Save timer references to avoid Sinon interfering (see GH-237).
+ */
+
+var Date = global.Date
+  , setTimeout = global.setTimeout
+  , setInterval = global.setInterval
+  , clearTimeout = global.clearTimeout
+  , clearInterval = global.clearInterval;
+
+/**
+ * Expose `Doc`.
+ */
+
+exports = module.exports = HTML;
+
+/**
+ * Stats template.
+ */
+
+var statsTemplate = '<ul id="mocha-stats">'
+  + '<li class="progress"><canvas width="40" height="40"></canvas></li>'
+  + '<li class="passes"><a href="#">passes:</a> <em>0</em></li>'
+  + '<li class="failures"><a href="#">failures:</a> <em>0</em></li>'
+  + '<li class="duration">duration: <em>0</em>s</li>'
+  + '</ul>';
+
+/**
+ * Initialize a new `Doc` reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function HTML(runner, root) {
+  Base.call(this, runner);
+
+  var self = this
+    , stats = this.stats
+    , total = runner.total
+    , stat = fragment(statsTemplate)
+    , items = stat.getElementsByTagName('li')
+    , passes = items[1].getElementsByTagName('em')[0]
+    , passesLink = items[1].getElementsByTagName('a')[0]
+    , failures = items[2].getElementsByTagName('em')[0]
+    , failuresLink = items[2].getElementsByTagName('a')[0]
+    , duration = items[3].getElementsByTagName('em')[0]
+    , canvas = stat.getElementsByTagName('canvas')[0]
+    , report = fragment('<ul id="mocha-report"></ul>')
+    , stack = [report]
+    , progress
+    , ctx
+
+  root = root || document.getElementById('mocha');
+
+  if (canvas.getContext) {
+    var ratio = window.devicePixelRatio || 1;
+    canvas.style.width = canvas.width;
+    canvas.style.height = canvas.height;
+    canvas.width *= ratio;
+    canvas.height *= ratio;
+    ctx = canvas.getContext('2d');
+    ctx.scale(ratio, ratio);
+    progress = new Progress;
+  }
+
+  if (!root) return error('#mocha div missing, add it to your document');
+
+  // pass toggle
+  on(passesLink, 'click', function(){
+    unhide();
+    var name = /pass/.test(report.className) ? '' : ' pass';
+    report.className = report.className.replace(/fail|pass/g, '') + name;
+    if (report.className.trim()) hideSuitesWithout('test pass');
+  });
+
+  // failure toggle
+  on(failuresLink, 'click', function(){
+    unhide();
+    var name = /fail/.test(report.className) ? '' : ' fail';
+    report.className = report.className.replace(/fail|pass/g, '') + name;
+    if (report.className.trim()) hideSuitesWithout('test fail');
+  });
+
+  root.appendChild(stat);
+  root.appendChild(report);
+
+  if (progress) progress.size(40);
+
+  runner.on('suite', function(suite){
+    if (suite.root) return;
+
+    // suite
+    var url = '?grep=' + encodeURIComponent(suite.fullTitle());
+    var el = fragment('<li class="suite"><h1><a href="%s">%s</a></h1></li>', url, escape(suite.title));
+
+    // container
+    stack[0].appendChild(el);
+    stack.unshift(document.createElement('ul'));
+    el.appendChild(stack[0]);
+  });
+
+  runner.on('suite end', function(suite){
+    if (suite.root) return;
+    stack.shift();
+  });
+
+  runner.on('fail', function(test, err){
+    if ('hook' == test.type) runner.emit('test end', test);
+  });
+
+  runner.on('test end', function(test){
+    window.scrollTo(0, document.body.scrollHeight);
+
+    // TODO: add to stats
+    var percent = stats.tests / this.total * 100 | 0;
+    if (progress) progress.update(percent).draw(ctx);
+
+    // update stats
+    var ms = new Date - stats.start;
+    text(passes, stats.passes);
+    text(failures, stats.failures);
+    text(duration, (ms / 1000).toFixed(2));
+
+    // test
+    if ('passed' == test.state) {
+      var el = fragment('<li class="test pass %e"><h2>%e<span class="duration">%ems</span> <a href="?grep=%e" class="replay">‣</a></h2></li>', test.speed, test.title, test.duration, encodeURIComponent(test.fullTitle()));
+    } else if (test.pending) {
+      var el = fragment('<li class="test pass pending"><h2>%e</h2></li>', test.title);
+    } else {
+      var el = fragment('<li class="test fail"><h2>%e <a href="?grep=%e" class="replay">‣</a></h2></li>', test.title, encodeURIComponent(test.fullTitle()));
+      var str = test.err.stack || test.err.toString();
+
+      // FF / Opera do not add the message
+      if (!~str.indexOf(test.err.message)) {
+        str = test.err.message + '\n' + str;
+      }
+
+      // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
+      // check for the result of the stringifying.
+      if ('[object Error]' == str) str = test.err.message;
+
+      // Safari doesn't give you a stack. Let's at least provide a source line.
+      if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) {
+        str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")";
+      }
+
+      el.appendChild(fragment('<pre class="error">%e</pre>', str));
+    }
+
+    // toggle code
+    // TODO: defer
+    if (!test.pending) {
+      var h2 = el.getElementsByTagName('h2')[0];
+
+      on(h2, 'click', function(){
+        pre.style.display = 'none' == pre.style.display
+          ? 'block'
+          : 'none';
+      });
+
+      var pre = fragment('<pre><code>%e</code></pre>', utils.clean(test.fn.toString()));
+      el.appendChild(pre);
+      pre.style.display = 'none';
+    }
+
+    // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack.
+    if (stack[0]) stack[0].appendChild(el);
+  });
+}
+
+/**
+ * Display error `msg`.
+ */
+
+function error(msg) {
+  document.body.appendChild(fragment('<div id="mocha-error">%s</div>', msg));
+}
+
+/**
+ * Return a DOM fragment from `html`.
+ */
+
+function fragment(html) {
+  var args = arguments
+    , div = document.createElement('div')
+    , i = 1;
+
+  div.innerHTML = html.replace(/%([se])/g, function(_, type){
+    switch (type) {
+      case 's': return String(args[i++]);
+      case 'e': return escape(args[i++]);
+    }
+  });
+
+  return div.firstChild;
+}
+
+/**
+ * Check for suites that do not have elements
+ * with `classname`, and hide them.
+ */
+
+function hideSuitesWithout(classname) {
+  var suites = document.getElementsByClassName('suite');
+  for (var i = 0; i < suites.length; i++) {
+    var els = suites[i].getElementsByClassName(classname);
+    if (0 == els.length) suites[i].className += ' hidden';
+  }
+}
+
+/**
+ * Unhide .hidden suites.
+ */
+
+function unhide() {
+  var els = document.getElementsByClassName('suite hidden');
+  for (var i = 0; i < els.length; ++i) {
+    els[i].className = els[i].className.replace('suite hidden', 'suite');
+  }
+}
+
+/**
+ * Set `el` text to `str`.
+ */
+
+function text(el, str) {
+  if (el.textContent) {
+    el.textContent = str;
+  } else {
+    el.innerText = str;
+  }
+}
+
+/**
+ * Listen on `event` with callback `fn`.
+ */
+
+function on(el, event, fn) {
+  if (el.addEventListener) {
+    el.addEventListener(event, fn, false);
+  } else {
+    el.attachEvent('on' + event, fn);
+  }
+}
+
+}); // module: reporters/html.js
+
+require.register("reporters/index.js", function(module, exports, require){
+
+exports.Base = require('./base');
+exports.Dot = require('./dot');
+exports.Doc = require('./doc');
+exports.TAP = require('./tap');
+exports.JSON = require('./json');
+exports.HTML = require('./html');
+exports.List = require('./list');
+exports.Min = require('./min');
+exports.Spec = require('./spec');
+exports.Nyan = require('./nyan');
+exports.XUnit = require('./xunit');
+exports.Markdown = require('./markdown');
+exports.Progress = require('./progress');
+exports.Landing = require('./landing');
+exports.JSONCov = require('./json-cov');
+exports.HTMLCov = require('./html-cov');
+exports.JSONStream = require('./json-stream');
+exports.Teamcity = require('./teamcity');
+
+}); // module: reporters/index.js
+
+require.register("reporters/json-cov.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base');
+
+/**
+ * Expose `JSONCov`.
+ */
+
+exports = module.exports = JSONCov;
+
+/**
+ * Initialize a new `JsCoverage` reporter.
+ *
+ * @param {Runner} runner
+ * @param {Boolean} output
+ * @api public
+ */
+
+function JSONCov(runner, output) {
+  var self = this
+    , output = 1 == arguments.length ? true : output;
+
+  Base.call(this, runner);
+
+  var tests = []
+    , failures = []
+    , passes = [];
+
+  runner.on('test end', function(test){
+    tests.push(test);
+  });
+
+  runner.on('pass', function(test){
+    passes.push(test);
+  });
+
+  runner.on('fail', function(test){
+    failures.push(test);
+  });
+
+  runner.on('end', function(){
+    var cov = global._$jscoverage || {};
+    var result = self.cov = map(cov);
+    result.stats = self.stats;
+    result.tests = tests.map(clean);
+    result.failures = failures.map(clean);
+    result.passes = passes.map(clean);
+    if (!output) return;
+    process.stdout.write(JSON.stringify(result, null, 2 ));
+  });
+}
+
+/**
+ * Map jscoverage data to a JSON structure
+ * suitable for reporting.
+ *
+ * @param {Object} cov
+ * @return {Object}
+ * @api private
+ */
+
+function map(cov) {
+  var ret = {
+      instrumentation: 'node-jscoverage'
+    , sloc: 0
+    , hits: 0
+    , misses: 0
+    , coverage: 0
+    , files: []
+  };
+
+  for (var filename in cov) {
+    var data = coverage(filename, cov[filename]);
+    ret.files.push(data);
+    ret.hits += data.hits;
+    ret.misses += data.misses;
+    ret.sloc += data.sloc;
+  }
+
+  ret.files.sort(function(a, b) {
+    return a.filename.localeCompare(b.filename);
+  });
+
+  if (ret.sloc > 0) {
+    ret.coverage = (ret.hits / ret.sloc) * 100;
+  }
+
+  return ret;
+};
+
+/**
+ * Map jscoverage data for a single source file
+ * to a JSON structure suitable for reporting.
+ *
+ * @param {String} filename name of the source file
+ * @param {Object} data jscoverage coverage data
+ * @return {Object}
+ * @api private
+ */
+
+function coverage(filename, data) {
+  var ret = {
+    filename: filename,
+    coverage: 0,
+    hits: 0,
+    misses: 0,
+    sloc: 0,
+    source: {}
+  };
+
+  data.source.forEach(function(line, num){
+    num++;
+
+    if (data[num] === 0) {
+      ret.misses++;
+      ret.sloc++;
+    } else if (data[num] !== undefined) {
+      ret.hits++;
+      ret.sloc++;
+    }
+
+    ret.source[num] = {
+        source: line
+      , coverage: data[num] === undefined
+        ? ''
+        : data[num]
+    };
+  });
+
+  ret.coverage = ret.hits / ret.sloc * 100;
+
+  return ret;
+}
+
+/**
+ * Return a plain-object representation of `test`
+ * free of cyclic properties etc.
+ *
+ * @param {Object} test
+ * @return {Object}
+ * @api private
+ */
+
+function clean(test) {
+  return {
+      title: test.title
+    , fullTitle: test.fullTitle()
+    , duration: test.duration
+  }
+}
+
+}); // module: reporters/json-cov.js
+
+require.register("reporters/json-stream.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , color = Base.color;
+
+/**
+ * Expose `List`.
+ */
+
+exports = module.exports = List;
+
+/**
+ * Initialize a new `List` test reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function List(runner) {
+  Base.call(this, runner);
+
+  var self = this
+    , stats = this.stats
+    , total = runner.total;
+
+  runner.on('start', function(){
+    console.log(JSON.stringify(['start', { total: total }]));
+  });
+
+  runner.on('pass', function(test){
+    console.log(JSON.stringify(['pass', clean(test)]));
+  });
+
+  runner.on('fail', function(test, err){
+    console.log(JSON.stringify(['fail', clean(test)]));
+  });
+
+  runner.on('end', function(){
+    process.stdout.write(JSON.stringify(['end', self.stats]));
+  });
+}
+
+/**
+ * Return a plain-object representation of `test`
+ * free of cyclic properties etc.
+ *
+ * @param {Object} test
+ * @return {Object}
+ * @api private
+ */
+
+function clean(test) {
+  return {
+      title: test.title
+    , fullTitle: test.fullTitle()
+    , duration: test.duration
+  }
+}
+}); // module: reporters/json-stream.js
+
+require.register("reporters/json.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , cursor = Base.cursor
+  , color = Base.color;
+
+/**
+ * Expose `JSON`.
+ */
+
+exports = module.exports = JSONReporter;
+
+/**
+ * Initialize a new `JSON` reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function JSONReporter(runner) {
+  var self = this;
+  Base.call(this, runner);
+
+  var tests = []
+    , failures = []
+    , passes = [];
+
+  runner.on('test end', function(test){
+    tests.push(test);
+  });
+
+  runner.on('pass', function(test){
+    passes.push(test);
+  });
+
+  runner.on('fail', function(test){
+    failures.push(test);
+  });
+
+  runner.on('end', function(){
+    var obj = {
+        stats: self.stats
+      , tests: tests.map(clean)
+      , failures: failures.map(clean)
+      , passes: passes.map(clean)
+    };
+
+    process.stdout.write(JSON.stringify(obj, null, 2));
+  });
+}
+
+/**
+ * Return a plain-object representation of `test`
+ * free of cyclic properties etc.
+ *
+ * @param {Object} test
+ * @return {Object}
+ * @api private
+ */
+
+function clean(test) {
+  return {
+      title: test.title
+    , fullTitle: test.fullTitle()
+    , duration: test.duration
+  }
+}
+}); // module: reporters/json.js
+
+require.register("reporters/landing.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , cursor = Base.cursor
+  , color = Base.color;
+
+/**
+ * Expose `Landing`.
+ */
+
+exports = module.exports = Landing;
+
+/**
+ * Airplane color.
+ */
+
+Base.colors.plane = 0;
+
+/**
+ * Airplane crash color.
+ */
+
+Base.colors['plane crash'] = 31;
+
+/**
+ * Runway color.
+ */
+
+Base.colors.runway = 90;
+
+/**
+ * Initialize a new `Landing` reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function Landing(runner) {
+  Base.call(this, runner);
+
+  var self = this
+    , stats = this.stats
+    , width = Base.window.width * .75 | 0
+    , total = runner.total
+    , stream = process.stdout
+    , plane = color('plane', '✈')
+    , crashed = -1
+    , n = 0;
+
+  function runway() {
+    var buf = Array(width).join('-');
+    return '  ' + color('runway', buf);
+  }
+
+  runner.on('start', function(){
+    stream.write('\n  ');
+    cursor.hide();
+  });
+
+  runner.on('test end', function(test){
+    // check if the plane crashed
+    var col = -1 == crashed
+      ? width * ++n / total | 0
+      : crashed;
+
+    // show the crash
+    if ('failed' == test.state) {
+      plane = color('plane crash', '✈');
+      crashed = col;
+    }
+
+    // render landing strip
+    stream.write('\u001b[4F\n\n');
+    stream.write(runway());
+    stream.write('\n  ');
+    stream.write(color('runway', Array(col).join('⋅')));
+    stream.write(plane)
+    stream.write(color('runway', Array(width - col).join('⋅') + '\n'));
+    stream.write(runway());
+    stream.write('\u001b[0m');
+  });
+
+  runner.on('end', function(){
+    cursor.show();
+    console.log();
+    self.epilogue();
+  });
+}
+
+/**
+ * Inherit from `Base.prototype`.
+ */
+
+function F(){};
+F.prototype = Base.prototype;
+Landing.prototype = new F;
+Landing.prototype.constructor = Landing;
+
+}); // module: reporters/landing.js
+
+require.register("reporters/list.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , cursor = Base.cursor
+  , color = Base.color;
+
+/**
+ * Expose `List`.
+ */
+
+exports = module.exports = List;
+
+/**
+ * Initialize a new `List` test reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function List(runner) {
+  Base.call(this, runner);
+
+  var self = this
+    , stats = this.stats
+    , n = 0;
+
+  runner.on('start', function(){
+    console.log();
+  });
+
+  runner.on('test', function(test){
+    process.stdout.write(color('pass', '    ' + test.fullTitle() + ': '));
+  });
+
+  runner.on('pending', function(test){
+    var fmt = color('checkmark', '  -')
+      + color('pending', ' %s');
+    console.log(fmt, test.fullTitle());
+  });
+
+  runner.on('pass', function(test){
+    var fmt = color('checkmark', '  '+Base.symbols.dot)
+      + color('pass', ' %s: ')
+      + color(test.speed, '%dms');
+    cursor.CR();
+    console.log(fmt, test.fullTitle(), test.duration);
+  });
+
+  runner.on('fail', function(test, err){
+    cursor.CR();
+    console.log(color('fail', '  %d) %s'), ++n, test.fullTitle());
+  });
+
+  runner.on('end', self.epilogue.bind(self));
+}
+
+/**
+ * Inherit from `Base.prototype`.
+ */
+
+function F(){};
+F.prototype = Base.prototype;
+List.prototype = new F;
+List.prototype.constructor = List;
+
+
+}); // module: reporters/list.js
+
+require.register("reporters/markdown.js", function(module, exports, require){
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , utils = require('../utils');
+
+/**
+ * Expose `Markdown`.
+ */
+
+exports = module.exports = Markdown;
+
+/**
+ * Initialize a new `Markdown` reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function Markdown(runner) {
+  Base.call(this, runner);
+
+  var self = this
+    , stats = this.stats
+    , level = 0
+    , buf = '';
+
+  function title(str) {
+    return Array(level).join('#') + ' ' + str;
+  }
+
+  function indent() {
+    return Array(level).join('  ');
+  }
+
+  function mapTOC(suite, obj) {
+    var ret = obj;
+    obj = obj[suite.title] = obj[suite.title] || { suite: suite };
+    suite.suites.forEach(function(suite){
+      mapTOC(suite, obj);
+    });
+    return ret;
+  }
+
+  function stringifyTOC(obj, level) {
+    ++level;
+    var buf = '';
+    var link;
+    for (var key in obj) {
+      if ('suite' == key) continue;
+      if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
+      if (key) buf += Array(level).join('  ') + link;
+      buf += stringifyTOC(obj[key], level);
+    }
+    --level;
+    return buf;
+  }
+
+  function generateTOC(suite) {
+    var obj = mapTOC(suite, {});
+    return stringifyTOC(obj, 0);
+  }
+
+  generateTOC(runner.suite);
+
+  runner.on('suite', function(suite){
+    ++level;
+    var slug = utils.slug(suite.fullTitle());
+    buf += '<a name="' + slug + '"></a>' + '\n';
+    buf += title(suite.title) + '\n';
+  });
+
+  runner.on('suite end', function(suite){
+    --level;
+  });
+
+  runner.on('pass', function(test){
+    var code = utils.clean(test.fn.toString());
+    buf += test.title + '.\n';
+    buf += '\n```js\n';
+    buf += code + '\n';
+    buf += '```\n\n';
+  });
+
+  runner.on('end', function(){
+    process.stdout.write('# TOC\n');
+    process.stdout.write(generateTOC(runner.suite));
+    process.stdout.write(buf);
+  });
+}
+}); // module: reporters/markdown.js
+
+require.register("reporters/min.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base');
+
+/**
+ * Expose `Min`.
+ */
+
+exports = module.exports = Min;
+
+/**
+ * Initialize a new `Min` minimal test reporter (best used with --watch).
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function Min(runner) {
+  Base.call(this, runner);
+  
+  runner.on('start', function(){
+    // clear screen
+    process.stdout.write('\u001b[2J');
+    // set cursor position
+    process.stdout.write('\u001b[1;3H');
+  });
+
+  runner.on('end', this.epilogue.bind(this));
+}
+
+/**
+ * Inherit from `Base.prototype`.
+ */
+
+function F(){};
+F.prototype = Base.prototype;
+Min.prototype = new F;
+Min.prototype.constructor = Min;
+
+}); // module: reporters/min.js
+
+require.register("reporters/nyan.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , color = Base.color;
+
+/**
+ * Expose `Dot`.
+ */
+
+exports = module.exports = NyanCat;
+
+/**
+ * Initialize a new `Dot` matrix test reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function NyanCat(runner) {
+  Base.call(this, runner);
+
+  var self = this
+    , stats = this.stats
+    , width = Base.window.width * .75 | 0
+    , rainbowColors = this.rainbowColors = self.generateColors()
+    , colorIndex = this.colorIndex = 0
+    , numerOfLines = this.numberOfLines = 4
+    , trajectories = this.trajectories = [[], [], [], []]
+    , nyanCatWidth = this.nyanCatWidth = 11
+    , trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth)
+    , scoreboardWidth = this.scoreboardWidth = 5
+    , tick = this.tick = 0
+    , n = 0;
+
+  runner.on('start', function(){
+    Base.cursor.hide();
+    self.draw('start');
+  });
+
+  runner.on('pending', function(test){
+    self.draw('pending');
+  });
+
+  runner.on('pass', function(test){
+    self.draw('pass');
+  });
+
+  runner.on('fail', function(test, err){
+    self.draw('fail');
+  });
+
+  runner.on('end', function(){
+    Base.cursor.show();
+    for (var i = 0; i < self.numberOfLines; i++) write('\n');
+    self.epilogue();
+  });
+}
+
+/**
+ * Draw the nyan cat with runner `status`.
+ *
+ * @param {String} status
+ * @api private
+ */
+
+NyanCat.prototype.draw = function(status){
+  this.appendRainbow();
+  this.drawScoreboard();
+  this.drawRainbow();
+  this.drawNyanCat(status);
+  this.tick = !this.tick;
+};
+
+/**
+ * Draw the "scoreboard" showing the number
+ * of passes, failures and pending tests.
+ *
+ * @api private
+ */
+
+NyanCat.prototype.drawScoreboard = function(){
+  var stats = this.stats;
+  var colors = Base.colors;
+
+  function draw(color, n) {
+    write(' ');
+    write('\u001b[' + color + 'm' + n + '\u001b[0m');
+    write('\n');
+  }
+
+  draw(colors.green, stats.passes);
+  draw(colors.fail, stats.failures);
+  draw(colors.pending, stats.pending);
+  write('\n');
+
+  this.cursorUp(this.numberOfLines);
+};
+
+/**
+ * Append the rainbow.
+ *
+ * @api private
+ */
+
+NyanCat.prototype.appendRainbow = function(){
+  var segment = this.tick ? '_' : '-';
+  var rainbowified = this.rainbowify(segment);
+
+  for (var index = 0; index < this.numberOfLines; index++) {
+    var trajectory = this.trajectories[index];
+    if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift();
+    trajectory.push(rainbowified);
+  }
+};
+
+/**
+ * Draw the rainbow.
+ *
+ * @api private
+ */
+
+NyanCat.prototype.drawRainbow = function(){
+  var self = this;
+
+  this.trajectories.forEach(function(line, index) {
+    write('\u001b[' + self.scoreboardWidth + 'C');
+    write(line.join(''));
+    write('\n');
+  });
+
+  this.cursorUp(this.numberOfLines);
+};
+
+/**
+ * Draw the nyan cat with `status`.
+ *
+ * @param {String} status
+ * @api private
+ */
+
+NyanCat.prototype.drawNyanCat = function(status) {
+  var self = this;
+  var startWidth = this.scoreboardWidth + this.trajectories[0].length;
+
+  [0, 1, 2, 3].forEach(function(index) {
+    write('\u001b[' + startWidth + 'C');
+
+    switch (index) {
+      case 0:
+        write('_,------,');
+        write('\n');
+        break;
+      case 1:
+        var padding = self.tick ? '  ' : '   ';
+        write('_|' + padding + '/\\_/\\ ');
+        write('\n');
+        break;
+      case 2:
+        var padding = self.tick ? '_' : '__';
+        var tail = self.tick ? '~' : '^';
+        var face;
+        switch (status) {
+          case 'pass':
+            face = '( ^ .^)';
+            break;
+          case 'fail':
+            face = '( o .o)';
+            break;
+          default:
+            face = '( - .-)';
+        }
+        write(tail + '|' + padding + face + ' ');
+        write('\n');
+        break;
+      case 3:
+        var padding = self.tick ? ' ' : '  ';
+        write(padding + '""  "" ');
+        write('\n');
+        break;
+    }
+  });
+
+  this.cursorUp(this.numberOfLines);
+};
+
+/**
+ * Move cursor up `n`.
+ *
+ * @param {Number} n
+ * @api private
+ */
+
+NyanCat.prototype.cursorUp = function(n) {
+  write('\u001b[' + n + 'A');
+};
+
+/**
+ * Move cursor down `n`.
+ *
+ * @param {Number} n
+ * @api private
+ */
+
+NyanCat.prototype.cursorDown = function(n) {
+  write('\u001b[' + n + 'B');
+};
+
+/**
+ * Generate rainbow colors.
+ *
+ * @return {Array}
+ * @api private
+ */
+
+NyanCat.prototype.generateColors = function(){
+  var colors = [];
+
+  for (var i = 0; i < (6 * 7); i++) {
+    var pi3 = Math.floor(Math.PI / 3);
+    var n = (i * (1.0 / 6));
+    var r = Math.floor(3 * Math.sin(n) + 3);
+    var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3);
+    var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3);
+    colors.push(36 * r + 6 * g + b + 16);
+  }
+
+  return colors;
+};
+
+/**
+ * Apply rainbow to the given `str`.
+ *
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+NyanCat.prototype.rainbowify = function(str){
+  var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length];
+  this.colorIndex += 1;
+  return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m';
+};
+
+/**
+ * Stdout helper.
+ */
+
+function write(string) {
+  process.stdout.write(string);
+}
+
+/**
+ * Inherit from `Base.prototype`.
+ */
+
+function F(){};
+F.prototype = Base.prototype;
+NyanCat.prototype = new F;
+NyanCat.prototype.constructor = NyanCat;
+
+
+}); // module: reporters/nyan.js
+
+require.register("reporters/progress.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , cursor = Base.cursor
+  , color = Base.color;
+
+/**
+ * Expose `Progress`.
+ */
+
+exports = module.exports = Progress;
+
+/**
+ * General progress bar color.
+ */
+
+Base.colors.progress = 90;
+
+/**
+ * Initialize a new `Progress` bar test reporter.
+ *
+ * @param {Runner} runner
+ * @param {Object} options
+ * @api public
+ */
+
+function Progress(runner, options) {
+  Base.call(this, runner);
+
+  var self = this
+    , options = options || {}
+    , stats = this.stats
+    , width = Base.window.width * .50 | 0
+    , total = runner.total
+    , complete = 0
+    , max = Math.max;
+
+  // default chars
+  options.open = options.open || '[';
+  options.complete = options.complete || 'â–¬';
+  options.incomplete = options.incomplete || Base.symbols.dot;
+  options.close = options.close || ']';
+  options.verbose = false;
+
+  // tests started
+  runner.on('start', function(){
+    console.log();
+    cursor.hide();
+  });
+
+  // tests complete
+  runner.on('test end', function(){
+    complete++;
+    var incomplete = total - complete
+      , percent = complete / total
+      , n = width * percent | 0
+      , i = width - n;
+
+    cursor.CR();
+    process.stdout.write('\u001b[J');
+    process.stdout.write(color('progress', '  ' + options.open));
+    process.stdout.write(Array(n).join(options.complete));
+    process.stdout.write(Array(i).join(options.incomplete));
+    process.stdout.write(color('progress', options.close));
+    if (options.verbose) {
+      process.stdout.write(color('progress', ' ' + complete + ' of ' + total));
+    }
+  });
+
+  // tests are complete, output some stats
+  // and the failures if any
+  runner.on('end', function(){
+    cursor.show();
+    console.log();
+    self.epilogue();
+  });
+}
+
+/**
+ * Inherit from `Base.prototype`.
+ */
+
+function F(){};
+F.prototype = Base.prototype;
+Progress.prototype = new F;
+Progress.prototype.constructor = Progress;
+
+
+}); // module: reporters/progress.js
+
+require.register("reporters/spec.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , cursor = Base.cursor
+  , color = Base.color;
+
+/**
+ * Expose `Spec`.
+ */
+
+exports = module.exports = Spec;
+
+/**
+ * Initialize a new `Spec` test reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function Spec(runner) {
+  Base.call(this, runner);
+
+  var self = this
+    , stats = this.stats
+    , indents = 0
+    , n = 0;
+
+  function indent() {
+    return Array(indents).join('  ')
+  }
+
+  runner.on('start', function(){
+    console.log();
+  });
+
+  runner.on('suite', function(suite){
+    ++indents;
+    console.log(color('suite', '%s%s'), indent(), suite.title);
+  });
+
+  runner.on('suite end', function(suite){
+    --indents;
+    if (1 == indents) console.log();
+  });
+
+  runner.on('test', function(test){
+    process.stdout.write(indent() + color('pass', '  â—¦ ' + test.title + ': '));
+  });
+
+  runner.on('pending', function(test){
+    var fmt = indent() + color('pending', '  - %s');
+    console.log(fmt, test.title);
+  });
+
+  runner.on('pass', function(test){
+    if ('fast' == test.speed) {
+      var fmt = indent()
+        + color('checkmark', '  ' + Base.symbols.ok)
+        + color('pass', ' %s ');
+      cursor.CR();
+      console.log(fmt, test.title);
+    } else {
+      var fmt = indent()
+        + color('checkmark', '  ' + Base.symbols.ok)
+        + color('pass', ' %s ')
+        + color(test.speed, '(%dms)');
+      cursor.CR();
+      console.log(fmt, test.title, test.duration);
+    }
+  });
+
+  runner.on('fail', function(test, err){
+    cursor.CR();
+    console.log(indent() + color('fail', '  %d) %s'), ++n, test.title);
+  });
+
+  runner.on('end', self.epilogue.bind(self));
+}
+
+/**
+ * Inherit from `Base.prototype`.
+ */
+
+function F(){};
+F.prototype = Base.prototype;
+Spec.prototype = new F;
+Spec.prototype.constructor = Spec;
+
+
+}); // module: reporters/spec.js
+
+require.register("reporters/tap.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , cursor = Base.cursor
+  , color = Base.color;
+
+/**
+ * Expose `TAP`.
+ */
+
+exports = module.exports = TAP;
+
+/**
+ * Initialize a new `TAP` reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function TAP(runner) {
+  Base.call(this, runner);
+
+  var self = this
+    , stats = this.stats
+    , n = 1
+    , passes = 0
+    , failures = 0;
+
+  runner.on('start', function(){
+    var total = runner.grepTotal(runner.suite);
+    console.log('%d..%d', 1, total);
+  });
+
+  runner.on('test end', function(){
+    ++n;
+  });
+
+  runner.on('pending', function(test){
+    console.log('ok %d %s # SKIP -', n, title(test));
+  });
+
+  runner.on('pass', function(test){
+    passes++;
+    console.log('ok %d %s', n, title(test));
+  });
+
+  runner.on('fail', function(test, err){
+    failures++;
+    console.log('not ok %d %s', n, title(test));
+    if (err.stack) console.log(err.stack.replace(/^/gm, '  '));
+  });
+
+  runner.on('end', function(){
+    console.log('# tests ' + (passes + failures));
+    console.log('# pass ' + passes);
+    console.log('# fail ' + failures);
+  });
+}
+
+/**
+ * Return a TAP-safe title of `test`
+ *
+ * @param {Object} test
+ * @return {String}
+ * @api private
+ */
+
+function title(test) {
+  return test.fullTitle().replace(/#/g, '');
+}
+
+}); // module: reporters/tap.js
+
+require.register("reporters/teamcity.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base');
+
+/**
+ * Expose `Teamcity`.
+ */
+
+exports = module.exports = Teamcity;
+
+/**
+ * Initialize a new `Teamcity` reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function Teamcity(runner) {
+  Base.call(this, runner);
+  var stats = this.stats;
+
+  runner.on('start', function() {
+    console.log("##teamcity[testSuiteStarted name='mocha.suite']");
+  });
+
+  runner.on('test', function(test) {
+    console.log("##teamcity[testStarted name='" + escape(test.fullTitle()) + "']");
+  });
+
+  runner.on('fail', function(test, err) {
+    console.log("##teamcity[testFailed name='" + escape(test.fullTitle()) + "' message='" + escape(err.message) + "']");
+  });
+
+  runner.on('pending', function(test) {
+    console.log("##teamcity[testIgnored name='" + escape(test.fullTitle()) + "' message='pending']");
+  });
+
+  runner.on('test end', function(test) {
+    console.log("##teamcity[testFinished name='" + escape(test.fullTitle()) + "' duration='" + test.duration + "']");
+  });
+
+  runner.on('end', function() {
+    console.log("##teamcity[testSuiteFinished name='mocha.suite' duration='" + stats.duration + "']");
+  });
+}
+
+/**
+ * Escape the given `str`.
+ */
+
+function escape(str) {
+  return str
+    .replace(/\|/g, "||")
+    .replace(/\n/g, "|n")
+    .replace(/\r/g, "|r")
+    .replace(/\[/g, "|[")
+    .replace(/\]/g, "|]")
+    .replace(/\u0085/g, "|x")
+    .replace(/\u2028/g, "|l")
+    .replace(/\u2029/g, "|p")
+    .replace(/'/g, "|'");
+}
+
+}); // module: reporters/teamcity.js
+
+require.register("reporters/xunit.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Base = require('./base')
+  , utils = require('../utils')
+  , escape = utils.escape;
+
+/**
+ * Save timer references to avoid Sinon interfering (see GH-237).
+ */
+
+var Date = global.Date
+  , setTimeout = global.setTimeout
+  , setInterval = global.setInterval
+  , clearTimeout = global.clearTimeout
+  , clearInterval = global.clearInterval;
+
+/**
+ * Expose `XUnit`.
+ */
+
+exports = module.exports = XUnit;
+
+/**
+ * Initialize a new `XUnit` reporter.
+ *
+ * @param {Runner} runner
+ * @api public
+ */
+
+function XUnit(runner) {
+  Base.call(this, runner);
+  var stats = this.stats
+    , tests = []
+    , self = this;
+
+  runner.on('pass', function(test){
+    tests.push(test);
+  });
+  
+  runner.on('fail', function(test){
+    tests.push(test);
+  });
+
+  runner.on('end', function(){
+    console.log(tag('testsuite', {
+        name: 'Mocha Tests'
+      , tests: stats.tests
+      , failures: stats.failures
+      , errors: stats.failures
+      , skip: stats.tests - stats.failures - stats.passes
+      , timestamp: (new Date).toUTCString()
+      , time: stats.duration / 1000
+    }, false));
+
+    tests.forEach(test);
+    console.log('</testsuite>');    
+  });
+}
+
+/**
+ * Inherit from `Base.prototype`.
+ */
+
+function F(){};
+F.prototype = Base.prototype;
+XUnit.prototype = new F;
+XUnit.prototype.constructor = XUnit;
+
+
+/**
+ * Output tag for the given `test.`
+ */
+
+function test(test) {
+  var attrs = {
+      classname: test.parent.fullTitle()
+    , name: test.title
+    , time: test.duration / 1000
+  };
+
+  if ('failed' == test.state) {
+    var err = test.err;
+    attrs.message = escape(err.message);
+    console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack))));
+  } else if (test.pending) {
+    console.log(tag('testcase', attrs, false, tag('skipped', {}, true)));
+  } else {
+    console.log(tag('testcase', attrs, true) );
+  }
+}
+
+/**
+ * HTML tag helper.
+ */
+
+function tag(name, attrs, close, content) {
+  var end = close ? '/>' : '>'
+    , pairs = []
+    , tag;
+
+  for (var key in attrs) {
+    pairs.push(key + '="' + escape(attrs[key]) + '"');
+  }
+
+  tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
+  if (content) tag += content + '</' + name + end;
+  return tag;
+}
+
+/**
+ * Return cdata escaped CDATA `str`.
+ */
+
+function cdata(str) {
+  return '<![CDATA[' + escape(str) + ']]>';
+}
+
+}); // module: reporters/xunit.js
+
+require.register("runnable.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var EventEmitter = require('browser/events').EventEmitter
+  , debug = require('browser/debug')('mocha:runnable')
+  , milliseconds = require('./ms');
+
+/**
+ * Save timer references to avoid Sinon interfering (see GH-237).
+ */
+
+var Date = global.Date
+  , setTimeout = global.setTimeout
+  , setInterval = global.setInterval
+  , clearTimeout = global.clearTimeout
+  , clearInterval = global.clearInterval;
+
+/**
+ * Object#toString().
+ */
+
+var toString = Object.prototype.toString;
+
+/**
+ * Expose `Runnable`.
+ */
+
+module.exports = Runnable;
+
+/**
+ * Initialize a new `Runnable` with the given `title` and callback `fn`.
+ *
+ * @param {String} title
+ * @param {Function} fn
+ * @api private
+ */
+
+function Runnable(title, fn) {
+  this.title = title;
+  this.fn = fn;
+  this.async = fn && fn.length;
+  this.sync = ! this.async;
+  this._timeout = 2000;
+  this._slow = 75;
+  this.timedOut = false;
+}
+
+/**
+ * Inherit from `EventEmitter.prototype`.
+ */
+
+function F(){};
+F.prototype = EventEmitter.prototype;
+Runnable.prototype = new F;
+Runnable.prototype.constructor = Runnable;
+
+
+/**
+ * Set & get timeout `ms`.
+ *
+ * @param {Number|String} ms
+ * @return {Runnable|Number} ms or self
+ * @api private
+ */
+
+Runnable.prototype.timeout = function(ms){
+  if (0 == arguments.length) return this._timeout;
+  if ('string' == typeof ms) ms = milliseconds(ms);
+  debug('timeout %d', ms);
+  this._timeout = ms;
+  if (this.timer) this.resetTimeout();
+  return this;
+};
+
+/**
+ * Set & get slow `ms`.
+ *
+ * @param {Number|String} ms
+ * @return {Runnable|Number} ms or self
+ * @api private
+ */
+
+Runnable.prototype.slow = function(ms){
+  if (0 === arguments.length) return this._slow;
+  if ('string' == typeof ms) ms = milliseconds(ms);
+  debug('timeout %d', ms);
+  this._slow = ms;
+  return this;
+};
+
+/**
+ * Return the full title generated by recursively
+ * concatenating the parent's full title.
+ *
+ * @return {String}
+ * @api public
+ */
+
+Runnable.prototype.fullTitle = function(){
+  return this.parent.fullTitle() + ' ' + this.title;
+};
+
+/**
+ * Clear the timeout.
+ *
+ * @api private
+ */
+
+Runnable.prototype.clearTimeout = function(){
+  clearTimeout(this.timer);
+};
+
+/**
+ * Inspect the runnable void of private properties.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Runnable.prototype.inspect = function(){
+  return JSON.stringify(this, function(key, val){
+    if ('_' == key[0]) return;
+    if ('parent' == key) return '#<Suite>';
+    if ('ctx' == key) return '#<Context>';
+    return val;
+  }, 2);
+};
+
+/**
+ * Reset the timeout.
+ *
+ * @api private
+ */
+
+Runnable.prototype.resetTimeout = function(){
+  var self = this
+    , ms = this.timeout();
+
+  this.clearTimeout();
+  if (ms) {
+    this.timer = setTimeout(function(){
+      self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
+      self.timedOut = true;
+    }, ms);
+  }
+};
+
+/**
+ * Run the test and invoke `fn(err)`.
+ *
+ * @param {Function} fn
+ * @api private
+ */
+
+Runnable.prototype.run = function(fn){
+  var self = this
+    , ms = this.timeout()
+    , start = new Date
+    , ctx = this.ctx
+    , finished
+    , emitted;
+
+  if (ctx) ctx.runnable(this);
+
+  // timeout
+  if (this.async) {
+    if (ms) {
+      this.timer = setTimeout(function(){
+        done(new Error('timeout of ' + ms + 'ms exceeded'));
+        self.timedOut = true;
+      }, ms);
+    }
+  }
+
+  // called multiple times
+  function multiple(err) {
+    if (emitted) return;
+    emitted = true;
+    self.emit('error', err || new Error('done() called multiple times'));
+  }
+
+  // finished
+  function done(err) {
+    if (self.timedOut) return;
+    if (finished) return multiple(err);
+    self.clearTimeout();
+    self.duration = new Date - start;
+    finished = true;
+    fn(err);
+  }
+
+  // for .resetTimeout()
+  this.callback = done;
+
+  // async
+  if (this.async) {
+    try {
+      this.fn.call(ctx, function(err){
+        if (err instanceof Error || toString.call(err) === "[object Error]") return done(err);
+        if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
+        done();
+      });
+    } catch (err) {
+      done(err);
+    }
+    return;
+  }
+
+  if (this.asyncOnly) {
+    return done(new Error('--async-only option in use without declaring `done()`'));
+  }
+
+  // sync
+  try {
+    if (!this.pending) this.fn.call(ctx);
+    this.duration = new Date - start;
+    fn();
+  } catch (err) {
+    fn(err);
+  }
+};
+
+}); // module: runnable.js
+
+require.register("runner.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var EventEmitter = require('browser/events').EventEmitter
+  , debug = require('browser/debug')('mocha:runner')
+  , Test = require('./test')
+  , utils = require('./utils')
+  , filter = utils.filter
+  , keys = utils.keys
+  , noop = function(){}
+  , immediately = global.setImmediate || process.nextTick;
+
+/**
+ * Non-enumerable globals.
+ */
+
+var globals = [
+  'setTimeout',
+  'clearTimeout',
+  'setInterval',
+  'clearInterval',
+  'XMLHttpRequest',
+  'Date'
+];
+
+/**
+ * Expose `Runner`.
+ */
+
+module.exports = Runner;
+
+/**
+ * Initialize a `Runner` for the given `suite`.
+ *
+ * Events:
+ *
+ *   - `start`  execution started
+ *   - `end`  execution complete
+ *   - `suite`  (suite) test suite execution started
+ *   - `suite end`  (suite) all tests (and sub-suites) have finished
+ *   - `test`  (test) test execution started
+ *   - `test end`  (test) test completed
+ *   - `hook`  (hook) hook execution started
+ *   - `hook end`  (hook) hook complete
+ *   - `pass`  (test) test passed
+ *   - `fail`  (test, err) test failed
+ *
+ * @api public
+ */
+
+function Runner(suite) {
+  var self = this;
+  this._globals = [];
+  this.suite = suite;
+  this.total = suite.total();
+  this.failures = 0;
+  this.on('test end', function(test){ self.checkGlobals(test); });
+  this.on('hook end', function(hook){ self.checkGlobals(hook); });
+  this.grep(/.*/);
+  this.globals(this.globalProps().concat(['errno']));
+}
+
+/**
+ * Inherit from `EventEmitter.prototype`.
+ */
+
+function F(){};
+F.prototype = EventEmitter.prototype;
+Runner.prototype = new F;
+Runner.prototype.constructor = Runner;
+
+
+/**
+ * Run tests with full titles matching `re`. Updates runner.total
+ * with number of tests matched.
+ *
+ * @param {RegExp} re
+ * @param {Boolean} invert
+ * @return {Runner} for chaining
+ * @api public
+ */
+
+Runner.prototype.grep = function(re, invert){
+  debug('grep %s', re);
+  this._grep = re;
+  this._invert = invert;
+  this.total = this.grepTotal(this.suite);
+  return this;
+};
+
+/**
+ * Returns the number of tests matching the grep search for the
+ * given suite.
+ *
+ * @param {Suite} suite
+ * @return {Number}
+ * @api public
+ */
+
+Runner.prototype.grepTotal = function(suite) {
+  var self = this;
+  var total = 0;
+
+  suite.eachTest(function(test){
+    var match = self._grep.test(test.fullTitle());
+    if (self._invert) match = !match;
+    if (match) total++;
+  });
+
+  return total;
+};
+
+/**
+ * Return a list of global properties.
+ *
+ * @return {Array}
+ * @api private
+ */
+
+Runner.prototype.globalProps = function() {
+  var props = utils.keys(global);
+
+  // non-enumerables
+  for (var i = 0; i < globals.length; ++i) {
+    if (~utils.indexOf(props, globals[i])) continue;
+    props.push(globals[i]);
+  }
+
+  return props;
+};
+
+/**
+ * Allow the given `arr` of globals.
+ *
+ * @param {Array} arr
+ * @return {Runner} for chaining
+ * @api public
+ */
+
+Runner.prototype.globals = function(arr){
+  if (0 == arguments.length) return this._globals;
+  debug('globals %j', arr);
+  utils.forEach(arr, function(arr){
+    this._globals.push(arr);
+  }, this);
+  return this;
+};
+
+/**
+ * Check for global variable leaks.
+ *
+ * @api private
+ */
+
+Runner.prototype.checkGlobals = function(test){
+  if (this.ignoreLeaks) return;
+  var ok = this._globals;
+  var globals = this.globalProps();
+  var isNode = process.kill;
+  var leaks;
+
+  // check length - 2 ('errno' and 'location' globals)
+  if (isNode && 1 == ok.length - globals.length) return
+  else if (2 == ok.length - globals.length) return;
+
+  leaks = filterLeaks(ok, globals);
+  this._globals = this._globals.concat(leaks);
+
+  if (leaks.length > 1) {
+    this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + ''));
+  } else if (leaks.length) {
+    this.fail(test, new Error('global leak detected: ' + leaks[0]));
+  }
+};
+
+/**
+ * Fail the given `test`.
+ *
+ * @param {Test} test
+ * @param {Error} err
+ * @api private
+ */
+
+Runner.prototype.fail = function(test, err){
+  ++this.failures;
+  test.state = 'failed';
+
+  if ('string' == typeof err) {
+    err = new Error('the string "' + err + '" was thrown, throw an Error :)');
+  }
+
+  this.emit('fail', test, err);
+};
+
+/**
+ * Fail the given `hook` with `err`.
+ *
+ * Hook failures (currently) hard-end due
+ * to that fact that a failing hook will
+ * surely cause subsequent tests to fail,
+ * causing jumbled reporting.
+ *
+ * @param {Hook} hook
+ * @param {Error} err
+ * @api private
+ */
+
+Runner.prototype.failHook = function(hook, err){
+  this.fail(hook, err);
+  this.emit('end');
+};
+
+/**
+ * Run hook `name` callbacks and then invoke `fn()`.
+ *
+ * @param {String} name
+ * @param {Function} function
+ * @api private
+ */
+
+Runner.prototype.hook = function(name, fn){
+  var suite = this.suite
+    , hooks = suite['_' + name]
+    , self = this
+    , timer;
+
+  function next(i) {
+    var hook = hooks[i];
+    if (!hook) return fn();
+    self.currentRunnable = hook;
+
+    self.emit('hook', hook);
+
+    hook.on('error', function(err){
+      self.failHook(hook, err);
+    });
+
+    hook.run(function(err){
+      hook.removeAllListeners('error');
+      var testError = hook.error();
+      if (testError) self.fail(self.test, testError);
+      if (err) return self.failHook(hook, err);
+      self.emit('hook end', hook);
+      next(++i);
+    });
+  }
+
+  immediately(function(){
+    next(0);
+  });
+};
+
+/**
+ * Run hook `name` for the given array of `suites`
+ * in order, and callback `fn(err)`.
+ *
+ * @param {String} name
+ * @param {Array} suites
+ * @param {Function} fn
+ * @api private
+ */
+
+Runner.prototype.hooks = function(name, suites, fn){
+  var self = this
+    , orig = this.suite;
+
+  function next(suite) {
+    self.suite = suite;
+
+    if (!suite) {
+      self.suite = orig;
+      return fn();
+    }
+
+    self.hook(name, function(err){
+      if (err) {
+        self.suite = orig;
+        return fn(err);
+      }
+
+      next(suites.pop());
+    });
+  }
+
+  next(suites.pop());
+};
+
+/**
+ * Run hooks from the top level down.
+ *
+ * @param {String} name
+ * @param {Function} fn
+ * @api private
+ */
+
+Runner.prototype.hookUp = function(name, fn){
+  var suites = [this.suite].concat(this.parents()).reverse();
+  this.hooks(name, suites, fn);
+};
+
+/**
+ * Run hooks from the bottom up.
+ *
+ * @param {String} name
+ * @param {Function} fn
+ * @api private
+ */
+
+Runner.prototype.hookDown = function(name, fn){
+  var suites = [this.suite].concat(this.parents());
+  this.hooks(name, suites, fn);
+};
+
+/**
+ * Return an array of parent Suites from
+ * closest to furthest.
+ *
+ * @return {Array}
+ * @api private
+ */
+
+Runner.prototype.parents = function(){
+  var suite = this.suite
+    , suites = [];
+  while (suite = suite.parent) suites.push(suite);
+  return suites;
+};
+
+/**
+ * Run the current test and callback `fn(err)`.
+ *
+ * @param {Function} fn
+ * @api private
+ */
+
+Runner.prototype.runTest = function(fn){
+  var test = this.test
+    , self = this;
+
+  if (this.asyncOnly) test.asyncOnly = true;
+
+  try {
+    test.on('error', function(err){
+      self.fail(test, err);
+    });
+    test.run(fn);
+  } catch (err) {
+    fn(err);
+  }
+};
+
+/**
+ * Run tests in the given `suite` and invoke
+ * the callback `fn()` when complete.
+ *
+ * @param {Suite} suite
+ * @param {Function} fn
+ * @api private
+ */
+
+Runner.prototype.runTests = function(suite, fn){
+  var self = this
+    , tests = suite.tests.slice()
+    , test;
+
+  function next(err) {
+    // if we bail after first err
+    if (self.failures && suite._bail) return fn();
+
+    // next test
+    test = tests.shift();
+
+    // all done
+    if (!test) return fn();
+
+    // grep
+    var match = self._grep.test(test.fullTitle());
+    if (self._invert) match = !match;
+    if (!match) return next();
+
+    // pending
+    if (test.pending) {
+      self.emit('pending', test);
+      self.emit('test end', test);
+      return next();
+    }
+
+    // execute test and hook(s)
+    self.emit('test', self.test = test);
+    self.hookDown('beforeEach', function(){
+      self.currentRunnable = self.test;
+      self.runTest(function(err){
+        test = self.test;
+
+        if (err) {
+          self.fail(test, err);
+          self.emit('test end', test);
+          return self.hookUp('afterEach', next);
+        }
+
+        test.state = 'passed';
+        self.emit('pass', test);
+        self.emit('test end', test);
+        self.hookUp('afterEach', next);
+      });
+    });
+  }
+
+  this.next = next;
+  next();
+};
+
+/**
+ * Run the given `suite` and invoke the
+ * callback `fn()` when complete.
+ *
+ * @param {Suite} suite
+ * @param {Function} fn
+ * @api private
+ */
+
+Runner.prototype.runSuite = function(suite, fn){
+  var total = this.grepTotal(suite)
+    , self = this
+    , i = 0;
+
+  debug('run suite %s', suite.fullTitle());
+
+  if (!total) return fn();
+
+  this.emit('suite', this.suite = suite);
+
+  function next() {
+    var curr = suite.suites[i++];
+    if (!curr) return done();
+    self.runSuite(curr, next);
+  }
+
+  function done() {
+    self.suite = suite;
+    self.hook('afterAll', function(){
+      self.emit('suite end', suite);
+      fn();
+    });
+  }
+
+  this.hook('beforeAll', function(){
+    self.runTests(suite, next);
+  });
+};
+
+/**
+ * Handle uncaught exceptions.
+ *
+ * @param {Error} err
+ * @api private
+ */
+
+Runner.prototype.uncaught = function(err){
+  debug('uncaught exception %s', err.message);
+  var runnable = this.currentRunnable;
+  if (!runnable || 'failed' == runnable.state) return;
+  runnable.clearTimeout();
+  err.uncaught = true;
+  this.fail(runnable, err);
+
+  // recover from test
+  if ('test' == runnable.type) {
+    this.emit('test end', runnable);
+    this.hookUp('afterEach', this.next);
+    return;
+  }
+
+  // bail on hooks
+  this.emit('end');
+};
+
+/**
+ * Run the root suite and invoke `fn(failures)`
+ * on completion.
+ *
+ * @param {Function} fn
+ * @return {Runner} for chaining
+ * @api public
+ */
+
+Runner.prototype.run = function(fn){
+  var self = this
+    , fn = fn || function(){};
+
+  function uncaught(err){
+    self.uncaught(err);
+  }
+
+  debug('start');
+
+  // callback
+  this.on('end', function(){
+    debug('end');
+    process.removeListener('uncaughtException', uncaught);
+    fn(self.failures);
+  });
+
+  // run suites
+  this.emit('start');
+  this.runSuite(this.suite, function(){
+    debug('finished running');
+    self.emit('end');
+  });
+
+  // uncaught exception
+  process.on('uncaughtException', uncaught);
+
+  return this;
+};
+
+/**
+ * Filter leaks with the given globals flagged as `ok`.
+ *
+ * @param {Array} ok
+ * @param {Array} globals
+ * @return {Array}
+ * @api private
+ */
+
+function filterLeaks(ok, globals) {
+  return filter(globals, function(key){
+    var matched = filter(ok, function(ok){
+      if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]);
+      // Opera and IE expose global variables for HTML element IDs (issue #243)
+      if (/^mocha-/.test(key)) return true;
+      return key == ok;
+    });
+    return matched.length == 0 && (!global.navigator || 'onerror' !== key);
+  });
+}
+
+}); // module: runner.js
+
+require.register("suite.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var EventEmitter = require('browser/events').EventEmitter
+  , debug = require('browser/debug')('mocha:suite')
+  , milliseconds = require('./ms')
+  , utils = require('./utils')
+  , Hook = require('./hook');
+
+/**
+ * Expose `Suite`.
+ */
+
+exports = module.exports = Suite;
+
+/**
+ * Create a new `Suite` with the given `title`
+ * and parent `Suite`. When a suite with the
+ * same title is already present, that suite
+ * is returned to provide nicer reporter
+ * and more flexible meta-testing.
+ *
+ * @param {Suite} parent
+ * @param {String} title
+ * @return {Suite}
+ * @api public
+ */
+
+exports.create = function(parent, title){
+  var suite = new Suite(title, parent.ctx);
+  suite.parent = parent;
+  if (parent.pending) suite.pending = true;
+  title = suite.fullTitle();
+  parent.addSuite(suite);
+  return suite;
+};
+
+/**
+ * Initialize a new `Suite` with the given
+ * `title` and `ctx`.
+ *
+ * @param {String} title
+ * @param {Context} ctx
+ * @api private
+ */
+
+function Suite(title, ctx) {
+  this.title = title;
+  this.ctx = ctx;
+  this.suites = [];
+  this.tests = [];
+  this.pending = false;
+  this._beforeEach = [];
+  this._beforeAll = [];
+  this._afterEach = [];
+  this._afterAll = [];
+  this.root = !title;
+  this._timeout = 2000;
+  this._slow = 75;
+  this._bail = false;
+}
+
+/**
+ * Inherit from `EventEmitter.prototype`.
+ */
+
+function F(){};
+F.prototype = EventEmitter.prototype;
+Suite.prototype = new F;
+Suite.prototype.constructor = Suite;
+
+
+/**
+ * Return a clone of this `Suite`.
+ *
+ * @return {Suite}
+ * @api private
+ */
+
+Suite.prototype.clone = function(){
+  var suite = new Suite(this.title);
+  debug('clone');
+  suite.ctx = this.ctx;
+  suite.timeout(this.timeout());
+  suite.slow(this.slow());
+  suite.bail(this.bail());
+  return suite;
+};
+
+/**
+ * Set timeout `ms` or short-hand such as "2s".
+ *
+ * @param {Number|String} ms
+ * @return {Suite|Number} for chaining
+ * @api private
+ */
+
+Suite.prototype.timeout = function(ms){
+  if (0 == arguments.length) return this._timeout;
+  if ('string' == typeof ms) ms = milliseconds(ms);
+  debug('timeout %d', ms);
+  this._timeout = parseInt(ms, 10);
+  return this;
+};
+
+/**
+ * Set slow `ms` or short-hand such as "2s".
+ *
+ * @param {Number|String} ms
+ * @return {Suite|Number} for chaining
+ * @api private
+ */
+
+Suite.prototype.slow = function(ms){
+  if (0 === arguments.length) return this._slow;
+  if ('string' == typeof ms) ms = milliseconds(ms);
+  debug('slow %d', ms);
+  this._slow = ms;
+  return this;
+};
+
+/**
+ * Sets whether to bail after first error.
+ *
+ * @parma {Boolean} bail
+ * @return {Suite|Number} for chaining
+ * @api private
+ */
+
+Suite.prototype.bail = function(bail){
+  if (0 == arguments.length) return this._bail;
+  debug('bail %s', bail);
+  this._bail = bail;
+  return this;
+};
+
+/**
+ * Run `fn(test[, done])` before running tests.
+ *
+ * @param {Function} fn
+ * @return {Suite} for chaining
+ * @api private
+ */
+
+Suite.prototype.beforeAll = function(fn){
+  if (this.pending) return this;
+  var hook = new Hook('"before all" hook', fn);
+  hook.parent = this;
+  hook.timeout(this.timeout());
+  hook.slow(this.slow());
+  hook.ctx = this.ctx;
+  this._beforeAll.push(hook);
+  this.emit('beforeAll', hook);
+  return this;
+};
+
+/**
+ * Run `fn(test[, done])` after running tests.
+ *
+ * @param {Function} fn
+ * @return {Suite} for chaining
+ * @api private
+ */
+
+Suite.prototype.afterAll = function(fn){
+  if (this.pending) return this;
+  var hook = new Hook('"after all" hook', fn);
+  hook.parent = this;
+  hook.timeout(this.timeout());
+  hook.slow(this.slow());
+  hook.ctx = this.ctx;
+  this._afterAll.push(hook);
+  this.emit('afterAll', hook);
+  return this;
+};
+
+/**
+ * Run `fn(test[, done])` before each test case.
+ *
+ * @param {Function} fn
+ * @return {Suite} for chaining
+ * @api private
+ */
+
+Suite.prototype.beforeEach = function(fn){
+  if (this.pending) return this;
+  var hook = new Hook('"before each" hook', fn);
+  hook.parent = this;
+  hook.timeout(this.timeout());
+  hook.slow(this.slow());
+  hook.ctx = this.ctx;
+  this._beforeEach.push(hook);
+  this.emit('beforeEach', hook);
+  return this;
+};
+
+/**
+ * Run `fn(test[, done])` after each test case.
+ *
+ * @param {Function} fn
+ * @return {Suite} for chaining
+ * @api private
+ */
+
+Suite.prototype.afterEach = function(fn){
+  if (this.pending) return this;
+  var hook = new Hook('"after each" hook', fn);
+  hook.parent = this;
+  hook.timeout(this.timeout());
+  hook.slow(this.slow());
+  hook.ctx = this.ctx;
+  this._afterEach.push(hook);
+  this.emit('afterEach', hook);
+  return this;
+};
+
+/**
+ * Add a test `suite`.
+ *
+ * @param {Suite} suite
+ * @return {Suite} for chaining
+ * @api private
+ */
+
+Suite.prototype.addSuite = function(suite){
+  suite.parent = this;
+  suite.timeout(this.timeout());
+  suite.slow(this.slow());
+  suite.bail(this.bail());
+  this.suites.push(suite);
+  this.emit('suite', suite);
+  return this;
+};
+
+/**
+ * Add a `test` to this suite.
+ *
+ * @param {Test} test
+ * @return {Suite} for chaining
+ * @api private
+ */
+
+Suite.prototype.addTest = function(test){
+  test.parent = this;
+  test.timeout(this.timeout());
+  test.slow(this.slow());
+  test.ctx = this.ctx;
+  this.tests.push(test);
+  this.emit('test', test);
+  return this;
+};
+
+/**
+ * Return the full title generated by recursively
+ * concatenating the parent's full title.
+ *
+ * @return {String}
+ * @api public
+ */
+
+Suite.prototype.fullTitle = function(){
+  if (this.parent) {
+    var full = this.parent.fullTitle();
+    if (full) return full + ' ' + this.title;
+  }
+  return this.title;
+};
+
+/**
+ * Return the total number of tests.
+ *
+ * @return {Number}
+ * @api public
+ */
+
+Suite.prototype.total = function(){
+  return utils.reduce(this.suites, function(sum, suite){
+    return sum + suite.total();
+  }, 0) + this.tests.length;
+};
+
+/**
+ * Iterates through each suite recursively to find
+ * all tests. Applies a function in the format
+ * `fn(test)`.
+ *
+ * @param {Function} fn
+ * @return {Suite}
+ * @api private
+ */
+
+Suite.prototype.eachTest = function(fn){
+  utils.forEach(this.tests, fn);
+  utils.forEach(this.suites, function(suite){
+    suite.eachTest(fn);
+  });
+  return this;
+};
+
+}); // module: suite.js
+
+require.register("test.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var Runnable = require('./runnable');
+
+/**
+ * Expose `Test`.
+ */
+
+module.exports = Test;
+
+/**
+ * Initialize a new `Test` with the given `title` and callback `fn`.
+ *
+ * @param {String} title
+ * @param {Function} fn
+ * @api private
+ */
+
+function Test(title, fn) {
+  Runnable.call(this, title, fn);
+  this.pending = !fn;
+  this.type = 'test';
+}
+
+/**
+ * Inherit from `Runnable.prototype`.
+ */
+
+function F(){};
+F.prototype = Runnable.prototype;
+Test.prototype = new F;
+Test.prototype.constructor = Test;
+
+
+}); // module: test.js
+
+require.register("utils.js", function(module, exports, require){
+
+/**
+ * Module dependencies.
+ */
+
+var fs = require('browser/fs')
+  , path = require('browser/path')
+  , join = path.join
+  , debug = require('browser/debug')('mocha:watch');
+
+/**
+ * Ignored directories.
+ */
+
+var ignore = ['node_modules', '.git'];
+
+/**
+ * Escape special characters in the given string of html.
+ *
+ * @param  {String} html
+ * @return {String}
+ * @api private
+ */
+
+exports.escape = function(html){
+  return String(html)
+    .replace(/&/g, '&amp;')
+    .replace(/"/g, '&quot;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;');
+};
+
+/**
+ * Array#forEach (<=IE8)
+ *
+ * @param {Array} array
+ * @param {Function} fn
+ * @param {Object} scope
+ * @api private
+ */
+
+exports.forEach = function(arr, fn, scope){
+  for (var i = 0, l = arr.length; i < l; i++)
+    fn.call(scope, arr[i], i);
+};
+
+/**
+ * Array#indexOf (<=IE8)
+ *
+ * @parma {Array} arr
+ * @param {Object} obj to find index of
+ * @param {Number} start
+ * @api private
+ */
+
+exports.indexOf = function(arr, obj, start){
+  for (var i = start || 0, l = arr.length; i < l; i++) {
+    if (arr[i] === obj)
+      return i;
+  }
+  return -1;
+};
+
+/**
+ * Array#reduce (<=IE8)
+ * 
+ * @param {Array} array
+ * @param {Function} fn
+ * @param {Object} initial value
+ * @api private
+ */
+
+exports.reduce = function(arr, fn, val){
+  var rval = val;
+
+  for (var i = 0, l = arr.length; i < l; i++) {
+    rval = fn(rval, arr[i], i, arr);
+  }
+
+  return rval;
+};
+
+/**
+ * Array#filter (<=IE8)
+ *
+ * @param {Array} array
+ * @param {Function} fn
+ * @api private
+ */
+
+exports.filter = function(arr, fn){
+  var ret = [];
+
+  for (var i = 0, l = arr.length; i < l; i++) {
+    var val = arr[i];
+    if (fn(val, i, arr)) ret.push(val);
+  }
+
+  return ret;
+};
+
+/**
+ * Object.keys (<=IE8)
+ *
+ * @param {Object} obj
+ * @return {Array} keys
+ * @api private
+ */
+
+exports.keys = Object.keys || function(obj) {
+  var keys = []
+    , has = Object.prototype.hasOwnProperty // for `window` on <=IE8
+
+  for (var key in obj) {
+    if (has.call(obj, key)) {
+      keys.push(key);
+    }
+  }
+
+  return keys;
+};
+
+/**
+ * Watch the given `files` for changes
+ * and invoke `fn(file)` on modification.
+ *
+ * @param {Array} files
+ * @param {Function} fn
+ * @api private
+ */
+
+exports.watch = function(files, fn){
+  var options = { interval: 100 };
+  files.forEach(function(file){
+    debug('file %s', file);
+    fs.watchFile(file, options, function(curr, prev){
+      if (prev.mtime < curr.mtime) fn(file);
+    });
+  });
+};
+
+/**
+ * Ignored files.
+ */
+
+function ignored(path){
+  return !~ignore.indexOf(path);
+}
+
+/**
+ * Lookup files in the given `dir`.
+ *
+ * @return {Array}
+ * @api private
+ */
+
+exports.files = function(dir, ret){
+  ret = ret || [];
+
+  fs.readdirSync(dir)
+  .filter(ignored)
+  .forEach(function(path){
+    path = join(dir, path);
+    if (fs.statSync(path).isDirectory()) {
+      exports.files(path, ret);
+    } else if (path.match(/\.(js|coffee)$/)) {
+      ret.push(path);
+    }
+  });
+
+  return ret;
+};
+
+/**
+ * Compute a slug from the given `str`.
+ *
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+exports.slug = function(str){
+  return str
+    .toLowerCase()
+    .replace(/ +/g, '-')
+    .replace(/[^-\w]/g, '');
+};
+
+/**
+ * Strip the function definition from `str`,
+ * and re-indent for pre whitespace.
+ */
+
+exports.clean = function(str) {
+  str = str
+    .replace(/^function *\(.*\) *{/, '')
+    .replace(/\s+\}$/, '');
+
+  var spaces = str.match(/^\n?( *)/)[1].length
+    , re = new RegExp('^ {' + spaces + '}', 'gm');
+
+  str = str.replace(re, '');
+
+  return exports.trim(str);
+};
+
+/**
+ * Escape regular expression characters in `str`.
+ *
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+exports.escapeRegexp = function(str){
+  return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
+};
+
+/**
+ * Trim the given `str`.
+ *
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+exports.trim = function(str){
+  return str.replace(/^\s+|\s+$/g, '');
+};
+
+/**
+ * Parse the given `qs`.
+ *
+ * @param {String} qs
+ * @return {Object}
+ * @api private
+ */
+
+exports.parseQuery = function(qs){
+  return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){
+    var i = pair.indexOf('=')
+      , key = pair.slice(0, i)
+      , val = pair.slice(++i);
+
+    obj[key] = decodeURIComponent(val);
+    return obj;
+  }, {});
+};
+
+/**
+ * Highlight the given string of `js`.
+ *
+ * @param {String} js
+ * @return {String}
+ * @api private
+ */
+
+function highlight(js) {
+  return js
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;')
+    .replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
+    .replace(/('.*?')/gm, '<span class="string">$1</span>')
+    .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
+    .replace(/(\d+)/gm, '<span class="number">$1</span>')
+    .replace(/\bnew *(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
+    .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
+}
+
+/**
+ * Highlight the contents of tag `name`.
+ *
+ * @param {String} name
+ * @api private
+ */
+
+exports.highlightTags = function(name) {
+  var code = document.getElementsByTagName(name);
+  for (var i = 0, len = code.length; i < len; ++i) {
+    code[i].innerHTML = highlight(code[i].innerHTML);
+  }
+};
+
+}); // module: utils.js
+/**
+ * Node shims.
+ *
+ * These are meant only to allow
+ * blanket_mocha.js to run untouched, not
+ * to allow running node code in
+ * the browser.
+ */
+
+process = {};
+process.exit = function(status){};
+process.stdout = {};
+global = window;
+
+/**
+ * next tick implementation.
+ */
+
+process.nextTick = (function(){
+  // postMessage behaves badly on IE8
+  if (window.ActiveXObject || !window.postMessage) {
+    return function(fn){ fn() };
+  }
+
+  // based on setZeroTimeout by David Baron
+  // - http://dbaron.org/log/20100309-faster-timeouts
+  var timeouts = []
+    , name = 'mocha-zero-timeout'
+
+  window.addEventListener('message', function(e){
+    if (e.source == window && e.data == name) {
+      if (e.stopPropagation) e.stopPropagation();
+      if (timeouts.length) timeouts.shift()();
+    }
+  }, true);
+
+  return function(fn){
+    timeouts.push(fn);
+    window.postMessage(name, '*');
+  }
+})();
+
+/**
+ * Remove uncaughtException listener.
+ */
+
+process.removeListener = function(e){
+  if ('uncaughtException' == e) {
+    window.onerror = null;
+  }
+};
+
+/**
+ * Implements uncaughtException listener.
+ */
+
+process.on = function(e, fn){
+  if ('uncaughtException' == e) {
+    window.onerror = function(err, url, line){
+      fn(new Error(err + ' (' + url + ':' + line + ')'));
+    };
+  }
+};
+
+// boot
+;(function(){
+
+  /**
+   * Expose mocha.
+   */
+
+  var Mocha = window.Mocha = require('mocha'),
+      mocha = window.mocha = new Mocha({ reporter: 'html' });
+
+  /**
+   * Override ui to ensure that the ui functions are initialized.
+   * Normally this would happen in Mocha.prototype.loadFiles.
+   */
+
+  mocha.ui = function(ui){
+    Mocha.prototype.ui.call(this, ui);
+    this.suite.emit('pre-require', window, null, this);
+    return this;
+  };
+
+  /**
+   * Setup mocha with the given setting options.
+   */
+
+  mocha.setup = function(opts){
+    if ('string' == typeof opts) opts = { ui: opts };
+    for (var opt in opts) this[opt](opts[opt]);
+    return this;
+  };
+
+  /**
+   * Run mocha, returning the Runner.
+   */
+
+  mocha.run = function(fn){
+    var options = mocha.options;
+    mocha.globals('location');
+
+    var query = Mocha.utils.parseQuery(window.location.search || '');
+    if (query.grep) mocha.grep(query.grep);
+    if (query.invert) mocha.invert();
+
+    return Mocha.prototype.run.call(mocha, function(){
+      Mocha.utils.highlightTags('code');
+      if (fn) fn();
+    });
+  };
+})();
+})();
\ No newline at end of file
diff --git a/tests/test.html b/tests/test.html
new file mode 100755
index 0000000..66391d2
--- /dev/null
+++ b/tests/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>

diff --git a/tests/test.js b/tests/test.js
new file mode 100755
index 0000000..f726680
--- /dev/null
+++ b/tests/test.js
@@ -0,0 +1,927 @@
+//
+// 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.
+//
+
+/**
+* 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 _username = 'marty2';
+var _email = 'marty2@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;
+		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:'Rocky'
+	}
+
+	client.createEntity(options, function (err, 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'+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, 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, 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/'+_username
+	};
+	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, 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, 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) {
+			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) {
+					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('newpassword', _newpassword);
+	marty.save(function(err){
+		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) {
+		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, 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, data) {
+		if (err) {
+			error('connection not created');
+			runner(step, marty);
+		} else {
+
+			//call succeeded, so pull the connections back down
+			marty.getConnections('likes', function (err, 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, 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, newdog) {
+				if (err) {
+					error('Create new entity to use for existing entity failed');
+				} else {
+					success('Create new entity to use for existing entity succeeded');
+
+					var newuuid = newdog.get('uuid');
+					if (newuuid === uuid) {
+						success('UUIDs of new and old entities match');
+					} else {
+						error('UUIDs of new and old entities do not match');
+					}
+
+					var breed = newdog.get('breed');
+					if (breed === 'mutt') {
+						success('attribute sucesfully set on new entity');
+					} else {
+						error('attribute not sucesfully set on new entity');
+					}
+
+					newdog.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, entity) {
+		if (err) {
+			error('Create new entity with no name failed');
+		} else {
+			success('Create new entity with no name succeeded');
+
+      entity.destroy();
+      runner(step);
+		}
+	});
+
+}
+
+});
\ No newline at end of file
diff --git a/usergrid.js b/usergrid.js
new file mode 100644
index 0000000..5989d8a
--- /dev/null
+++ b/usergrid.js
@@ -0,0 +1,3286 @@
+/*
+ * 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.
+ */
+var UsergridEventable = function() {
+    throw Error("'UsergridEventable' is not intended to be invoked directly");
+};
+
+UsergridEventable.prototype = {
+    bind: function(event, fn) {
+        this._events = this._events || {};
+        this._events[event] = this._events[event] || [];
+        this._events[event].push(fn);
+    },
+    unbind: function(event, fn) {
+        this._events = this._events || {};
+        if (event in this._events === false) return;
+        this._events[event].splice(this._events[event].indexOf(fn), 1);
+    },
+    trigger: function(event) {
+        this._events = this._events || {};
+        if (event in this._events === false) return;
+        for (var i = 0; i < this._events[event].length; i++) {
+            this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
+        }
+    }
+};
+
+UsergridEventable.mixin = function(destObject) {
+    var props = [ "bind", "unbind", "trigger" ];
+    for (var i = 0; i < props.length; i++) {
+        if (props[i] in destObject.prototype) {
+            console.warn("overwriting '" + props[i] + "' on '" + destObject.name + "'.");
+            console.warn("the previous version can be found at '_" + props[i] + "' on '" + destObject.name + "'.");
+            destObject.prototype["_" + props[i]] = destObject.prototype[props[i]];
+        }
+        destObject.prototype[props[i]] = UsergridEventable.prototype[props[i]];
+    }
+};
+
+(function() {
+    var name = "Logger", global = this, overwrittenName = global[name], exports;
+    /* logging */
+    function Logger(name) {
+        this.logEnabled = true;
+        this.init(name, true);
+    }
+    Logger.METHODS = [ "log", "error", "warn", "info", "debug", "assert", "clear", "count", "dir", "dirxml", "exception", "group", "groupCollapsed", "groupEnd", "profile", "profileEnd", "table", "time", "timeEnd", "trace" ];
+    Logger.prototype.init = function(name, logEnabled) {
+        this.name = name || "UNKNOWN";
+        this.logEnabled = logEnabled || true;
+        var addMethod = function(method) {
+            this[method] = this.createLogMethod(method);
+        }.bind(this);
+        Logger.METHODS.forEach(addMethod);
+    };
+    Logger.prototype.createLogMethod = function(method) {
+        return Logger.prototype.log.bind(this, method);
+    };
+    Logger.prototype.prefix = function(method, args) {
+        var prepend = "[" + method.toUpperCase() + "][" + name + "]:	";
+        if ([ "log", "error", "warn", "info" ].indexOf(method) !== -1) {
+            if ("string" === typeof args[0]) {
+                args[0] = prepend + args[0];
+            } else {
+                args.unshift(prepend);
+            }
+        }
+        return args;
+    };
+    Logger.prototype.log = function() {
+        var args = [].slice.call(arguments);
+        method = args.shift();
+        if (Logger.METHODS.indexOf(method) === -1) {
+            method = "log";
+        }
+        if (!(this.logEnabled && console && console[method])) return;
+        args = this.prefix(method, args);
+        console[method].apply(console, args);
+    };
+    Logger.prototype.setLogEnabled = function(logEnabled) {
+        this.logEnabled = logEnabled || true;
+    };
+    Logger.mixin = function(destObject) {
+        destObject.__logger = new Logger(destObject.name || "UNKNOWN");
+        var addMethod = function(method) {
+            if (method in destObject.prototype) {
+                console.warn("overwriting '" + method + "' on '" + destObject.name + "'.");
+                console.warn("the previous version can be found at '_" + method + "' on '" + destObject.name + "'.");
+                destObject.prototype["_" + method] = destObject.prototype[method];
+            }
+            destObject.prototype[method] = destObject.__logger.createLogMethod(method);
+        };
+        Logger.METHODS.forEach(addMethod);
+    };
+    global[name] = Logger;
+    global[name].noConflict = function() {
+        if (overwrittenName) {
+            global[name] = overwrittenName;
+        }
+        return Logger;
+    };
+    return global[name];
+})();
+
+(function(global) {
+    var name = "Promise", overwrittenName = global[name], exports;
+    function Promise() {
+        this.complete = false;
+        this.error = null;
+        this.result = null;
+        this.callbacks = [];
+    }
+    Promise.prototype.then = function(callback, context) {
+        var f = function() {
+            return callback.apply(context, arguments);
+        };
+        if (this.complete) {
+            f(this.error, this.result);
+        } else {
+            this.callbacks.push(f);
+        }
+    };
+    Promise.prototype.done = function(error, result) {
+        this.complete = true;
+        this.error = error;
+        this.result = result;
+        if (this.callbacks) {
+            for (var i = 0; i < this.callbacks.length; i++) this.callbacks[i](error, result);
+            this.callbacks.length = 0;
+        }
+    };
+    Promise.join = function(promises) {
+        var p = new Promise(), total = promises.length, completed = 0, errors = [], results = [];
+        function notifier(i) {
+            return function(error, result) {
+                completed += 1;
+                errors[i] = error;
+                results[i] = result;
+                if (completed === total) {
+                    p.done(errors, results);
+                }
+            };
+        }
+        for (var i = 0; i < total; i++) {
+            promises[i]().then(notifier(i));
+        }
+        return p;
+    };
+    Promise.chain = function(promises, error, result) {
+        var p = new Promise();
+        if (promises === null || promises.length === 0) {
+            p.done(error, result);
+        } else {
+            promises[0](error, result).then(function(res, err) {
+                promises.splice(0, 1);
+                if (promises) {
+                    Promise.chain(promises, res, err).then(function(r, e) {
+                        p.done(r, e);
+                    });
+                } else {
+                    p.done(res, err);
+                }
+            });
+        }
+        return p;
+    };
+    global[name] = Promise;
+    global[name].noConflict = function() {
+        if (overwrittenName) {
+            global[name] = overwrittenName;
+        }
+        return Promise;
+    };
+    return global[name];
+})(this);
+
+(function() {
+    var name = "Ajax", global = this, overwrittenName = global[name], exports;
+    function partial() {
+        var args = Array.prototype.slice.call(arguments);
+        var fn = args.shift();
+        return fn.bind(this, args);
+    }
+    function Ajax() {
+        this.logger = new global.Logger(name);
+        var self = this;
+        function encode(data) {
+            var result = "";
+            if (typeof data === "string") {
+                result = data;
+            } else {
+                var e = encodeURIComponent;
+                for (var i in data) {
+                    if (data.hasOwnProperty(i)) {
+                        result += "&" + e(i) + "=" + e(data[i]);
+                    }
+                }
+            }
+            return result;
+        }
+        function request(m, u, d) {
+            var p = new Promise(), timeout;
+            self.logger.time(m + " " + u);
+            (function(xhr) {
+                xhr.onreadystatechange = function() {
+                    if (this.readyState === 4) {
+                        self.logger.timeEnd(m + " " + u);
+                        clearTimeout(timeout);
+                        p.done(null, this);
+                    }
+                };
+                xhr.onerror = function(response) {
+                    clearTimeout(timeout);
+                    p.done(response, null);
+                };
+                xhr.oncomplete = function(response) {
+                    clearTimeout(timeout);
+                    self.logger.timeEnd(m + " " + u);
+                    self.info("%s request to %s returned %s", m, u, this.status);
+                };
+                xhr.open(m, u);
+                if (d) {
+                    if ("object" === typeof d) {
+                        d = JSON.stringify(d);
+                    }
+                    xhr.setRequestHeader("Content-Type", "application/json");
+                    xhr.setRequestHeader("Accept", "application/json");
+                }
+                timeout = setTimeout(function() {
+                    xhr.abort();
+                    p.done("API Call timed out.", null);
+                }, 3e4);
+                xhr.send(encode(d));
+            })(new XMLHttpRequest());
+            return p;
+        }
+        this.request = request;
+        this.get = partial(request, "GET");
+        this.post = partial(request, "POST");
+        this.put = partial(request, "PUT");
+        this.delete = partial(request, "DELETE");
+    }
+    global[name] = new Ajax();
+    global[name].noConflict = function() {
+        if (overwrittenName) {
+            global[name] = overwrittenName;
+        }
+        return exports;
+    };
+    return global[name];
+})();
+
+/*
+ *  This module is a collection of classes designed to make working with
+ *  the Appigee App Services API as easy as possible.
+ *  Learn more at http://Usergrid.com/docs/usergrid
+ *
+ *   Copyright 2012 Usergrid Corporation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  @author rod simpson (rod@Usergrid.com)
+ *  @author matt dobson (matt@Usergrid.com)
+ *  @author ryan bridges (rbridges@Usergrid.com)
+ */
+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);
+        returnValue = callback.apply(context, params);
+    }
+    return returnValue;
+}
+
+(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) {
+        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.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;
+    };
+    Usergrid.Response = function(err, response) {
+        var p = new Promise();
+        var data = null;
+        try {
+            data = JSON.parse(response.responseText);
+        } catch (e) {
+            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:
+            this.success = true;
+            break;
+
+          case 400:
+          case 500:
+          case 300:
+          case 100:
+          default:
+            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);
+
+(function() {
+    var name = "Client", global = this, overwrittenName = global[name], exports;
+    var AUTH_ERRORS = [ "auth_expired_session_token", "auth_missing_credentials", "auth_unverified_oath", "expired_token", "unauthorized", "auth_invalid" ];
+    Usergrid.Client = function(options) {
+        this.URI = options.URI || "https://api.usergrid.com";
+        if (options.orgName) {
+            this.set("orgName", options.orgName);
+        }
+        if (options.appName) {
+            this.set("appName", options.appName);
+        }
+        if (options.qs) {
+            this.setObject("default_qs", options.qs);
+        }
+        this.buildCurl = options.buildCurl || false;
+        this.logging = options.logging || false;
+    };
+    /*
+   *  Main function for making requests to the API.  Can be called directly.
+   *
+   *  options object:
+   *  `method` - http method (GET, POST, PUT, or DELETE), defaults to GET
+   *  `qs` - object containing querystring values to be appended to the uri
+   *  `body` - object containing entity body for POST and PUT requests
+   *  `endpoint` - API endpoint, for example 'users/fred'
+   *  `mQuery` - boolean, set to true if running management query, defaults to false
+   *
+   *  @method request
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+    Usergrid.Client.prototype.request = function(options, callback) {
+        var method = options.method || "GET";
+        var endpoint = options.endpoint;
+        var body = options.body || {};
+        var qs = options.qs || {};
+        var mQuery = options.mQuery || false;
+        var orgName = this.get("orgName");
+        var appName = this.get("appName");
+        var default_qs = this.getObject("default_qs");
+        var uri;
+        /*var logoutCallback=function(){
+        if (typeof(this.logoutCallback) === 'function') {
+            return this.logoutCallback(true, 'no_org_or_app_name_specified');
+        }
+    }.bind(this);*/
+        if (!mQuery && !orgName && !appName) {
+            return logoutCallback();
+        }
+        if (mQuery) {
+            uri = this.URI + "/" + endpoint;
+        } else {
+            uri = this.URI + "/" + orgName + "/" + appName + "/" + endpoint;
+        }
+        if (this.getToken()) {
+            qs.access_token = this.getToken();
+        }
+        if (default_qs) {
+            qs = propCopy(qs, default_qs);
+        }
+        var self = this;
+        var req = new Usergrid.Request(method, uri, qs, body, function(err, response) {
+            /*if (AUTH_ERRORS.indexOf(response.error) !== -1) {
+            return logoutCallback();
+        }*/
+            if (err) {
+                doCallback(callback, [ err, response, self ], self);
+            } else {
+                doCallback(callback, [ null, response, self ], self);
+            }
+        });
+    };
+    /*
+   *  function for building asset urls
+   *
+   *  @method buildAssetURL
+   *  @public
+   *  @params {string} uuid
+   *  @return {string} assetURL
+   */
+    Usergrid.Client.prototype.buildAssetURL = function(uuid) {
+        var self = this;
+        var qs = {};
+        var assetURL = this.URI + "/" + this.orgName + "/" + this.appName + "/assets/" + uuid + "/data";
+        if (self.getToken()) {
+            qs.access_token = self.getToken();
+        }
+        var encoded_params = encodeParams(qs);
+        if (encoded_params) {
+            assetURL += "?" + encoded_params;
+        }
+        return assetURL;
+    };
+    /*
+   *  Main function for creating new groups. Call this directly.
+   *
+   *  @method createGroup
+   *  @public
+   *  @params {string} path
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+    Usergrid.Client.prototype.createGroup = function(options, callback) {
+        var group = new Usergrid.Group({
+            path: options.path,
+            client: this,
+            data: options
+        });
+        group.save(function(err, response) {
+            doCallback(callback, [ err, response, group ], group);
+        });
+    };
+    /*
+   *  Main function for creating new entities - should be called directly.
+   *
+   *  options object: options {data:{'type':'collection_type', 'key':'value'}, uuid:uuid}}
+   *
+   *  @method createEntity
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+    Usergrid.Client.prototype.createEntity = function(options, callback) {
+        var entity = new Usergrid.Entity({
+            client: this,
+            data: options
+        });
+        entity.save(function(err, response) {
+            doCallback(callback, [ err, response, entity ], entity);
+        });
+    };
+    /*
+   *  Main function for getting existing entities - should be called directly.
+   *
+   *  You must supply a uuid or (username or name). Username only applies to users.
+   *  Name applies to all custom entities
+   *
+   *  options object: options {data:{'type':'collection_type', 'name':'value', 'username':'value'}, uuid:uuid}}
+   *
+   *  @method createEntity
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+    Usergrid.Client.prototype.getEntity = function(options, callback) {
+        var entity = new Usergrid.Entity({
+            client: this,
+            data: options
+        });
+        entity.fetch(function(err, response) {
+            doCallback(callback, [ err, response, entity ], entity);
+        });
+    };
+    /*
+   *  Main function for restoring an entity from serialized data.
+   *
+   *  serializedObject should have come from entityObject.serialize();
+   *
+   *  @method restoreEntity
+   *  @public
+   *  @param {string} serializedObject
+   *  @return {object} Entity Object
+   */
+    Usergrid.Client.prototype.restoreEntity = function(serializedObject) {
+        var data = JSON.parse(serializedObject);
+        var options = {
+            client: this,
+            data: data
+        };
+        var entity = new Usergrid.Entity(options);
+        return entity;
+    };
+    /*
+   *  Main function for creating new counters - should be called directly.
+   *
+   *  options object: options {timestamp:0, category:'value', counters:{name : value}}
+   *
+   *  @method createCounter
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, response, counter)
+   */
+    Usergrid.Client.prototype.createCounter = function(options, callback) {
+        var counter = new Usergrid.Counter({
+            client: this,
+            data: options
+        });
+        counter.save(callback);
+    };
+    /*
+   *  Main function for creating new assets - should be called directly.
+   *
+   *  options object: options {name:"photo.jpg", path:"/user/uploads", "content-type":"image/jpeg", owner:"F01DE600-0000-0000-0000-000000000000", file: FileOrBlobObject }
+   *
+   *  @method createCounter
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, response, counter)
+   */
+    Usergrid.Client.prototype.createAsset = function(options, callback) {
+        var file = options.file;
+        if (file) {
+            options.name = options.name || file.name;
+            options["content-type"] = options["content-type"] || file.type;
+            options.path = options.path || "/";
+            delete options.file;
+        }
+        var asset = new Usergrid.Asset({
+            client: this,
+            data: options
+        });
+        asset.save(function(err, response, asset) {
+            if (file && !err) {
+                asset.upload(file, callback);
+            } else {
+                doCallback(callback, [ err, response, asset ], asset);
+            }
+        });
+    };
+    /*
+   *  Main function for creating new collections - should be called directly.
+   *
+   *  options object: options {client:client, type: type, qs:qs}
+   *
+   *  @method createCollection
+   *  @public
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+    Usergrid.Client.prototype.createCollection = function(options, callback) {
+        options.client = this;
+        return new Usergrid.Collection(options, function(err, data, collection) {
+            console.log("createCollection", arguments);
+            doCallback(callback, [ err, collection, data ]);
+        });
+    };
+    /*
+   *  Main function for restoring a collection from serialized data.
+   *
+   *  serializedObject should have come from collectionObject.serialize();
+   *
+   *  @method restoreCollection
+   *  @public
+   *  @param {string} serializedObject
+   *  @return {object} Collection Object
+   */
+    Usergrid.Client.prototype.restoreCollection = function(serializedObject) {
+        var data = JSON.parse(serializedObject);
+        data.client = this;
+        var collection = new Usergrid.Collection(data);
+        return collection;
+    };
+    /*
+   *  Main function for retrieving a user's activity feed.
+   *
+   *  @method getFeedForUser
+   *  @public
+   *  @params {string} username
+   *  @param {function} callback
+   *  @return {callback} callback(err, data, activities)
+   */
+    Usergrid.Client.prototype.getFeedForUser = function(username, callback) {
+        var options = {
+            method: "GET",
+            endpoint: "users/" + username + "/feed"
+        };
+        this.request(options, function(err, data) {
+            if (err) {
+                doCallback(callback, [ err ]);
+            } else {
+                doCallback(callback, [ err, data, data.getEntities() ]);
+            }
+        });
+    };
+    /*
+   *  Function for creating new activities for the current user - should be called directly.
+   *
+   *  //user can be any of the following: "me", a uuid, a username
+   *  Note: the "me" alias will reference the currently logged in user (e.g. 'users/me/activties')
+   *
+   *  //build a json object that looks like this:
+   *  var options =
+   *  {
+   *    "actor" : {
+   *      "displayName" :"myusername",
+   *      "uuid" : "myuserid",
+   *      "username" : "myusername",
+   *      "email" : "myemail",
+   *      "picture": "http://path/to/picture",
+   *      "image" : {
+   *          "duration" : 0,
+   *          "height" : 80,
+   *          "url" : "http://www.gravatar.com/avatar/",
+   *          "width" : 80
+   *      },
+   *    },
+   *    "verb" : "post",
+   *    "content" : "My cool message",
+   *    "lat" : 48.856614,
+   *    "lon" : 2.352222
+   *  }
+   *
+   *  @method createEntity
+   *  @public
+   *  @params {string} user // "me", a uuid, or a username
+   *  @params {object} options
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+    Usergrid.Client.prototype.createUserActivity = function(user, options, callback) {
+        options.type = "users/" + user + "/activities";
+        options = {
+            client: this,
+            data: options
+        };
+        var entity = new Usergrid.Entity(options);
+        entity.save(function(err, data) {
+            doCallback(callback, [ err, data, entity ]);
+        });
+    };
+    /*
+   *  Function for creating user activities with an associated user entity.
+   *
+   *  user object:
+   *  The user object passed into this function is an instance of Usergrid.Entity.
+   *
+   *  @method createUserActivityWithEntity
+   *  @public
+   *  @params {object} user
+   *  @params {string} content
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+    Usergrid.Client.prototype.createUserActivityWithEntity = function(user, content, callback) {
+        var username = user.get("username");
+        var options = {
+            actor: {
+                displayName: username,
+                uuid: user.get("uuid"),
+                username: username,
+                email: user.get("email"),
+                picture: user.get("picture"),
+                image: {
+                    duration: 0,
+                    height: 80,
+                    url: user.get("picture"),
+                    width: 80
+                }
+            },
+            verb: "post",
+            content: content
+        };
+        this.createUserActivity(username, options, callback);
+    };
+    /*
+   *  A private method to get call timing of last call
+   */
+    Usergrid.Client.prototype.calcTimeDiff = function() {
+        var seconds = 0;
+        var time = this._end - this._start;
+        try {
+            seconds = (time / 10 / 60).toFixed(2);
+        } catch (e) {
+            return 0;
+        }
+        return seconds;
+    };
+    /*
+   *  A public method to store the OAuth token for later use - uses localstorage if available
+   *
+   *  @method setToken
+   *  @public
+   *  @params {string} token
+   *  @return none
+   */
+    Usergrid.Client.prototype.setToken = function(token) {
+        this.set("token", token);
+    };
+    /*
+   *  A public method to get the OAuth token
+   *
+   *  @method getToken
+   *  @public
+   *  @return {string} token
+   */
+    Usergrid.Client.prototype.getToken = function() {
+        return this.get("token");
+    };
+    Usergrid.Client.prototype.setObject = function(key, value) {
+        if (value) {
+            value = JSON.stringify(value);
+        }
+        this.set(key, value);
+    };
+    Usergrid.Client.prototype.set = function(key, value) {
+        var keyStore = "apigee_" + key;
+        this[key] = value;
+        if (typeof Storage !== "undefined") {
+            if (value) {
+                localStorage.setItem(keyStore, value);
+            } else {
+                localStorage.removeItem(keyStore);
+            }
+        }
+    };
+    Usergrid.Client.prototype.getObject = function(key) {
+        return JSON.parse(this.get(key));
+    };
+    Usergrid.Client.prototype.get = function(key) {
+        var keyStore = "apigee_" + key;
+        var value = null;
+        if (this[key]) {
+            value = this[key];
+        } else if (typeof Storage !== "undefined") {
+            value = localStorage.getItem(keyStore);
+        }
+        return value;
+    };
+    /*
+   * A public facing helper method for signing up users
+   *
+   * @method signup
+   * @public
+   * @params {string} username
+   * @params {string} password
+   * @params {string} email
+   * @params {string} name
+   * @param {function} callback
+   * @return {callback} callback(err, data)
+   */
+    Usergrid.Client.prototype.signup = function(username, password, email, name, callback) {
+        var self = this;
+        var options = {
+            type: "users",
+            username: username,
+            password: password,
+            email: email,
+            name: name
+        };
+        this.createEntity(options, callback);
+    };
+    /*
+   *
+   *  A public method to log in an app user - stores the token for later use
+   *
+   *  @method login
+   *  @public
+   *  @params {string} username
+   *  @params {string} password
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+    Usergrid.Client.prototype.login = function(username, password, callback) {
+        var self = this;
+        var options = {
+            method: "POST",
+            endpoint: "token",
+            body: {
+                username: username,
+                password: password,
+                grant_type: "password"
+            }
+        };
+        self.request(options, function(err, response) {
+            var user = {};
+            if (err) {
+                if (self.logging) console.log("error trying to log user in");
+            } else {
+                var options = {
+                    client: self,
+                    data: response.user
+                };
+                user = new Usergrid.Entity(options);
+                self.setToken(response.access_token);
+            }
+            doCallback(callback, [ err, response, user ]);
+        });
+    };
+    Usergrid.Client.prototype.reAuthenticateLite = function(callback) {
+        var self = this;
+        var options = {
+            method: "GET",
+            endpoint: "management/me",
+            mQuery: true
+        };
+        this.request(options, function(err, response) {
+            if (err && self.logging) {
+                console.log("error trying to re-authenticate user");
+            } else {
+                self.setToken(response.data.access_token);
+            }
+            doCallback(callback, [ err ]);
+        });
+    };
+    Usergrid.Client.prototype.reAuthenticate = function(email, callback) {
+        var self = this;
+        var options = {
+            method: "GET",
+            endpoint: "management/users/" + email,
+            mQuery: true
+        };
+        this.request(options, function(err, response) {
+            var organizations = {};
+            var applications = {};
+            var user = {};
+            var data;
+            if (err && self.logging) {
+                console.log("error trying to full authenticate user");
+            } else {
+                data = response.data;
+                self.setToken(data.token);
+                self.set("email", data.email);
+                localStorage.setItem("accessToken", data.token);
+                localStorage.setItem("userUUID", data.uuid);
+                localStorage.setItem("userEmail", data.email);
+                var userData = {
+                    username: data.username,
+                    email: data.email,
+                    name: data.name,
+                    uuid: data.uuid
+                };
+                var options = {
+                    client: self,
+                    data: userData
+                };
+                user = new Usergrid.Entity(options);
+                organizations = data.organizations;
+                var org = "";
+                try {
+                    var existingOrg = self.get("orgName");
+                    org = organizations[existingOrg] ? organizations[existingOrg] : organizations[Object.keys(organizations)[0]];
+                    self.set("orgName", org.name);
+                } catch (e) {
+                    err = true;
+                    if (self.logging) {
+                        console.log("error selecting org");
+                    }
+                }
+                applications = self.parseApplicationsArray(org);
+                self.selectFirstApp(applications);
+                self.setObject("organizations", organizations);
+                self.setObject("applications", applications);
+            }
+            doCallback(callback, [ err, data, user, organizations, applications ], self);
+        });
+    };
+    /*
+   *  A public method to log in an app user with facebook - stores the token for later use
+   *
+   *  @method loginFacebook
+   *  @public
+   *  @params {string} username
+   *  @params {string} password
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+    Usergrid.Client.prototype.loginFacebook = function(facebookToken, callback) {
+        var self = this;
+        var options = {
+            method: "GET",
+            endpoint: "auth/facebook",
+            qs: {
+                fb_access_token: facebookToken
+            }
+        };
+        this.request(options, function(err, data) {
+            var user = {};
+            if (err && self.logging) {
+                console.log("error trying to log user in");
+            } else {
+                var options = {
+                    client: self,
+                    data: data.user
+                };
+                user = new Usergrid.Entity(options);
+                self.setToken(data.access_token);
+            }
+            doCallback(callback, [ err, data, user ], self);
+        });
+    };
+    /*
+   *  A public method to get the currently logged in user entity
+   *
+   *  @method getLoggedInUser
+   *  @public
+   *  @param {function} callback
+   *  @return {callback} callback(err, data)
+   */
+    Usergrid.Client.prototype.getLoggedInUser = function(callback) {
+        var self = this;
+        if (!this.getToken()) {
+            doCallback(callback, [ new UsergridError("Access Token not set"), null, self ], self);
+        } else {
+            var options = {
+                method: "GET",
+                endpoint: "users/me"
+            };
+            this.request(options, function(err, response) {
+                if (err) {
+                    if (self.logging) {
+                        console.log("error trying to log user in");
+                    }
+                    console.error(err, response);
+                    doCallback(callback, [ err, response, self ], self);
+                } else {
+                    var options = {
+                        client: self,
+                        data: response.getEntity()
+                    };
+                    var user = new Usergrid.Entity(options);
+                    doCallback(callback, [ null, response, user ], self);
+                }
+            });
+        }
+    };
+    /*
+   *  A public method to test if a user is logged in - does not guarantee that the token is still valid,
+   *  but rather that one exists
+   *
+   *  @method isLoggedIn
+   *  @public
+   *  @return {boolean} Returns true the user is logged in (has token and uuid), false if not
+   */
+    Usergrid.Client.prototype.isLoggedIn = function() {
+        var token = this.getToken();
+        return "undefined" !== typeof token && token !== null;
+    };
+    /*
+   *  A public method to log out an app user - clears all user fields from client
+   *
+   *  @method logout
+   *  @public
+   *  @return none
+   */
+    Usergrid.Client.prototype.logout = function() {
+        this.setToken();
+    };
+    /*
+   *  A public method to destroy access tokens on the server
+   *
+   *  @method logout
+   *  @public
+   *  @param {string} username	the user associated with the token to revoke
+   *  @param {string} token set to 'null' to revoke the token of the currently logged in user
+   *    or set to token value to revoke a specific token
+   *  @param {string} revokeAll set to 'true' to revoke all tokens for the user
+   *  @return none
+   */
+    Usergrid.Client.prototype.destroyToken = function(username, token, revokeAll, callback) {
+        var options = {
+            client: self,
+            method: "PUT"
+        };
+        if (revokeAll === true) {
+            options.endpoint = "users/" + username + "/revoketokens";
+        } else if (token === null) {
+            options.endpoint = "users/" + username + "/revoketoken?token=" + this.getToken();
+        } else {
+            options.endpoint = "users/" + username + "/revoketoken?token=" + token;
+        }
+        this.request(options, function(err, data) {
+            if (err) {
+                if (self.logging) {
+                    console.log("error destroying access token");
+                }
+                doCallback(callback, [ err, data, null ], self);
+            } else {
+                if (revokeAll === true) {
+                    console.log("all user tokens invalidated");
+                } else {
+                    console.log("token invalidated");
+                }
+                doCallback(callback, [ err, data, null ], self);
+            }
+        });
+    };
+    /*
+   *  A public method to log out an app user - clears all user fields from client
+   *  and destroys the access token on the server.
+   *
+   *  @method logout
+   *  @public
+   *  @param {string} username the user associated with the token to revoke
+   *  @param {string} token set to 'null' to revoke the token of the currently logged in user
+   *   or set to token value to revoke a specific token
+   *  @param {string} revokeAll set to 'true' to revoke all tokens for the user
+   *  @return none
+   */
+    Usergrid.Client.prototype.logoutAndDestroyToken = function(username, token, revokeAll, callback) {
+        if (username === null) {
+            console.log("username required to revoke tokens");
+        } else {
+            this.destroyToken(username, token, revokeAll, callback);
+            if (revokeAll === true || token === this.getToken() || token === null) {
+                this.setToken(null);
+            }
+        }
+    };
+    /*
+   *  A private method to build the curl call to display on the command line
+   *
+   *  @method buildCurlCall
+   *  @private
+   *  @param {object} options
+   *  @return {string} curl
+   */
+    Usergrid.Client.prototype.buildCurlCall = function(options) {
+        var curl = [ "curl" ];
+        var method = (options.method || "GET").toUpperCase();
+        var body = options.body;
+        var uri = options.uri;
+        curl.push("-X");
+        curl.push([ "POST", "PUT", "DELETE" ].indexOf(method) >= 0 ? method : "GET");
+        curl.push(uri);
+        if ("object" === typeof body && Object.keys(body).length > 0 && [ "POST", "PUT" ].indexOf(method) !== -1) {
+            curl.push("-d");
+            curl.push("'" + JSON.stringify(body) + "'");
+        }
+        curl = curl.join(" ");
+        console.log(curl);
+        return curl;
+    };
+    Usergrid.Client.prototype.getDisplayImage = function(email, picture, size) {
+        size = size || 50;
+        var image = "https://apigee.com/usergrid/images/user_profile.png";
+        try {
+            if (picture) {
+                image = picture;
+            } else if (email.length) {
+                image = "https://secure.gravatar.com/avatar/" + MD5(email) + "?s=" + size + encodeURI("&d=https://apigee.com/usergrid/images/user_profile.png");
+            }
+        } catch (e) {} finally {
+            return image;
+        }
+    };
+    global[name] = Usergrid.Client;
+    global[name].noConflict = function() {
+        if (overwrittenName) {
+            global[name] = overwrittenName;
+        }
+        return exports;
+    };
+    return global[name];
+})();
+
+var ENTITY_SYSTEM_PROPERTIES = [ "metadata", "created", "modified", "oldpassword", "newpassword", "type", "activated", "uuid" ];
+
+/*
+ *  A class to Model a Usergrid Entity.
+ *  Set the type and uuid of entity in the 'data' json object
+ *
+ *  @constructor
+ *  @param {object} options {client:client, data:{'type':'collection_type', uuid:'uuid', 'key':'value'}}
+ */
+Usergrid.Entity = function(options) {
+    this._data = {};
+    this._client = undefined;
+    if (options) {
+        this.set(options.data || {});
+        this._client = options.client || {};
+    }
+};
+
+/*
+ *  method to determine whether or not the passed variable is a Usergrid Entity
+ *
+ *  @method isEntity
+ *  @public
+ *  @params {any} obj - any variable
+ *  @return {boolean} Returns true or false
+ */
+Usergrid.Entity.isEntity = function(obj) {
+    return obj && obj instanceof Usergrid.Entity;
+};
+
+/*
+ *  method to determine whether or not the passed variable is a Usergrid Entity
+ *  That has been saved.
+ *
+ *  @method isPersistedEntity
+ *  @public
+ *  @params {any} obj - any variable
+ *  @return {boolean} Returns true or false
+ */
+Usergrid.Entity.isPersistedEntity = function(obj) {
+    return isEntity(obj) && isUUID(obj.get("uuid"));
+};
+
+/*
+ *  returns a serialized version of the entity object
+ *
+ *  Note: use the client.restoreEntity() function to restore
+ *
+ *  @method serialize
+ *  @return {string} data
+ */
+Usergrid.Entity.prototype.serialize = function() {
+    return JSON.stringify(this._data);
+};
+
+/*
+ *  gets a specific field or the entire data object. If null or no argument
+ *  passed, will return all data, else, will return a specific field
+ *
+ *  @method get
+ *  @param {string} field
+ *  @return {string} || {object} data
+ */
+Usergrid.Entity.prototype.get = function(key) {
+    var value;
+    if (arguments.length === 0) {
+        value = this._data;
+    } else if (arguments.length > 1) {
+        key = [].slice.call(arguments).reduce(function(p, c, i, a) {
+            if (c instanceof Array) {
+                p = p.concat(c);
+            } else {
+                p.push(c);
+            }
+            return p;
+        }, []);
+    }
+    if (key instanceof Array) {
+        var self = this;
+        value = key.map(function(k) {
+            return self.get(k);
+        });
+    } else if ("undefined" !== typeof key) {
+        value = this._data[key];
+    }
+    return value;
+};
+
+/*
+ *  adds a specific key value pair or object to the Entity's data
+ *  is additive - will not overwrite existing values unless they
+ *  are explicitly specified
+ *
+ *  @method set
+ *  @param {string} key || {object}
+ *  @param {string} value
+ *  @return none
+ */
+Usergrid.Entity.prototype.set = function(key, value) {
+    if (typeof key === "object") {
+        for (var field in key) {
+            this._data[field] = key[field];
+        }
+    } else if (typeof key === "string") {
+        if (value === null) {
+            delete this._data[key];
+        } else {
+            this._data[key] = value;
+        }
+    } else {
+        this._data = {};
+    }
+};
+
+Usergrid.Entity.prototype.getEndpoint = function() {
+    var type = this.get("type"), nameProperties = [ "uuid", "name" ], name;
+    if (type === undefined) {
+        throw new UsergridError("cannot fetch entity, no entity type specified", "no_type_specified");
+    } else if (/^users?$/.test(type)) {
+        nameProperties.unshift("username");
+    }
+    name = this.get(nameProperties).filter(function(x) {
+        return x !== null && "undefined" !== typeof x;
+    }).shift();
+    return name ? [ type, name ].join("/") : type;
+};
+
+/*
+ *  Saves the entity back to the database
+ *
+ *  @method save
+ *  @public
+ *  @param {function} callback
+ *  @return {callback} callback(err, response, self)
+ */
+Usergrid.Entity.prototype.save = function(callback) {
+    var self = this, type = this.get("type"), method = "POST", entityId = this.get("uuid"), changePassword, entityData = this.get(), options = {
+        method: method,
+        endpoint: type
+    };
+    if (entityId) {
+        options.method = "PUT";
+        options.endpoint += "/" + entityId;
+    }
+    options.body = Object.keys(entityData).filter(function(key) {
+        return ENTITY_SYSTEM_PROPERTIES.indexOf(key) === -1;
+    }).reduce(function(data, key) {
+        data[key] = entityData[key];
+        return data;
+    }, {});
+    self._client.request(options, function(err, response) {
+        var entity = response.getEntity();
+        if (entity) {
+            self.set(entity);
+            self.set("type", /^\//.test(response.path) ? response.path.substring(1) : response.path);
+        }
+        if (err && self._client.logging) {
+            console.log("could not save entity");
+        }
+        doCallback(callback, [ err, response, self ], self);
+    });
+};
+
+/*
+ *
+ * Updates the user's password
+ */
+Usergrid.Entity.prototype.changePassword = function(oldpassword, newpassword, callback) {
+    var self = this;
+    if ("function" === typeof oldpassword && callback === undefined) {
+        callback = oldpassword;
+        oldpassword = self.get("oldpassword");
+        newpassword = self.get("newpassword");
+    }
+    self.set({
+        password: null,
+        oldpassword: null,
+        newpassword: null
+    });
+    if (/^users?$/.test(self.get("type")) && oldpassword && newpassword) {
+        var options = {
+            method: "PUT",
+            endpoint: "users/" + self.get("uuid") + "/password",
+            body: {
+                uuid: self.get("uuid"),
+                username: self.get("username"),
+                oldpassword: oldpassword,
+                newpassword: newpassword
+            }
+        };
+        self._client.request(options, function(err, response) {
+            if (err && self._client.logging) {
+                console.log("could not update user");
+            }
+            doCallback(callback, [ err, response, self ], self);
+        });
+    } else {
+        throw new UsergridInvalidArgumentError("Invalid arguments passed to 'changePassword'");
+    }
+};
+
+/*
+ *  refreshes the entity by making a GET call back to the database
+ *
+ *  @method fetch
+ *  @public
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Entity.prototype.fetch = function(callback) {
+    var endpoint, self = this;
+    endpoint = this.getEndpoint();
+    var options = {
+        method: "GET",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, response) {
+        var entity = response.getEntity();
+        if (entity) {
+            self.set(entity);
+        }
+        doCallback(callback, [ err, response, self ], self);
+    });
+};
+
+/*
+ *  deletes the entity from the database - will only delete
+ *  if the object has a valid uuid
+ *
+ *  @method destroy
+ *  @public
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.destroy = function(callback) {
+    var self = this;
+    var endpoint = this.getEndpoint();
+    var options = {
+        method: "DELETE",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, response) {
+        if (!err) {
+            self.set(null);
+        }
+        doCallback(callback, [ err, response, self ], self);
+    });
+};
+
+/*
+ *  connects one entity to another
+ *
+ *  @method connect
+ *  @public
+ *  @param {string} connection
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.connect = function(connection, entity, callback) {
+    this.addOrRemoveConnection("POST", connection, entity, callback);
+};
+
+/*
+ *  disconnects one entity from another
+ *
+ *  @method disconnect
+ *  @public
+ *  @param {string} connection
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.disconnect = function(connection, entity, callback) {
+    this.addOrRemoveConnection("DELETE", connection, entity, callback);
+};
+
+/*
+ *  adds or removes a connection between two entities
+ *
+ *  @method addOrRemoveConnection
+ *  @public
+ *  @param {string} method
+ *  @param {string} connection
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.addOrRemoveConnection = function(method, connection, entity, callback) {
+    var self = this;
+    if ([ "POST", "DELETE" ].indexOf(method.toUpperCase()) == -1) {
+        throw new UsergridInvalidArgumentError("invalid method for connection call. must be 'POST' or 'DELETE'");
+    }
+    var connecteeType = entity.get("type");
+    var connectee = this.getEntityId(entity);
+    if (!connectee) {
+        throw new UsergridInvalidArgumentError("connectee could not be identified");
+    }
+    var connectorType = this.get("type");
+    var connector = this.getEntityId(this);
+    if (!connector) {
+        throw new UsergridInvalidArgumentError("connector could not be identified");
+    }
+    var endpoint = [ connectorType, connector, connection, connecteeType, connectee ].join("/");
+    var options = {
+        method: method,
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, response) {
+        if (err && self._client.logging) {
+            console.log("There was an error with the connection call");
+        }
+        doCallback(callback, [ err, response, self ], self);
+    });
+};
+
+/*
+ *  returns a unique identifier for an entity
+ *
+ *  @method connect
+ *  @public
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ *
+ */
+Usergrid.Entity.prototype.getEntityId = function(entity) {
+    var id;
+    if (isUUID(entity.get("uuid"))) {
+        id = entity.get("uuid");
+    } else if (this.get("type") === "users" || this.get("type") === "user") {
+        id = entity.get("username");
+    } else {
+        id = entity.get("name");
+    }
+    return id;
+};
+
+/*
+ *  gets an entities connections
+ *
+ *  @method getConnections
+ *  @public
+ *  @param {string} connection
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data, connections)
+ *
+ */
+Usergrid.Entity.prototype.getConnections = function(connection, callback) {
+    var self = this;
+    var connectorType = this.get("type");
+    var connector = this.getEntityId(this);
+    if (!connector) {
+        if (typeof callback === "function") {
+            var error = "Error in getConnections - no uuid specified.";
+            if (self._client.logging) {
+                console.log(error);
+            }
+            doCallback(callback, [ true, error ], self);
+        }
+        return;
+    }
+    var endpoint = connectorType + "/" + connector + "/" + connection + "/";
+    var options = {
+        method: "GET",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log("entity could not be connected");
+        }
+        self[connection] = {};
+        var length = data && data.entities ? data.entities.length : 0;
+        for (var i = 0; i < length; i++) {
+            if (data.entities[i].type === "user") {
+                self[connection][data.entities[i].username] = data.entities[i];
+            } else {
+                self[connection][data.entities[i].name] = data.entities[i];
+            }
+        }
+        doCallback(callback, [ err, data, data.entities ], self);
+    });
+};
+
+Usergrid.Entity.prototype.getGroups = function(callback) {
+    var self = this;
+    var endpoint = "users" + "/" + this.get("uuid") + "/groups";
+    var options = {
+        method: "GET",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log("entity could not be connected");
+        }
+        self.groups = data.entities;
+        doCallback(callback, [ err, data, data.entities ], self);
+    });
+};
+
+Usergrid.Entity.prototype.getActivities = function(callback) {
+    var self = this;
+    var endpoint = this.get("type") + "/" + this.get("uuid") + "/activities";
+    var options = {
+        method: "GET",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log("entity could not be connected");
+        }
+        for (var entity in data.entities) {
+            data.entities[entity].createdDate = new Date(data.entities[entity].created).toUTCString();
+        }
+        self.activities = data.entities;
+        doCallback(callback, [ err, data, data.entities ], self);
+    });
+};
+
+Usergrid.Entity.prototype.getFollowing = function(callback) {
+    var self = this;
+    var endpoint = "users" + "/" + this.get("uuid") + "/following";
+    var options = {
+        method: "GET",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log("could not get user following");
+        }
+        for (var entity in data.entities) {
+            data.entities[entity].createdDate = new Date(data.entities[entity].created).toUTCString();
+            var image = self._client.getDisplayImage(data.entities[entity].email, data.entities[entity].picture);
+            data.entities[entity]._portal_image_icon = image;
+        }
+        self.following = data.entities;
+        doCallback(callback, [ err, data, data.entities ], self);
+    });
+};
+
+Usergrid.Entity.prototype.getFollowers = function(callback) {
+    var self = this;
+    var endpoint = "users" + "/" + this.get("uuid") + "/followers";
+    var options = {
+        method: "GET",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log("could not get user followers");
+        }
+        for (var entity in data.entities) {
+            data.entities[entity].createdDate = new Date(data.entities[entity].created).toUTCString();
+            var image = self._client.getDisplayImage(data.entities[entity].email, data.entities[entity].picture);
+            data.entities[entity]._portal_image_icon = image;
+        }
+        self.followers = data.entities;
+        doCallback(callback, [ err, data, data.entities ], self);
+    });
+};
+
+Usergrid.Client.prototype.createRole = function(roleName, permissions, callback) {
+    var options = {
+        type: "role",
+        name: roleName
+    };
+    this.createEntity(options, function(err, response, entity) {
+        if (err) {
+            doCallback(callback, [ err, response, self ]);
+        } else {
+            entity.assignPermissions(permissions, function(err, data) {
+                if (err) {
+                    doCallback(callback, [ err, response, self ]);
+                } else {
+                    doCallback(callback, [ err, data, data.data ], self);
+                }
+            });
+        }
+    });
+};
+
+Usergrid.Entity.prototype.getRoles = function(callback) {
+    var self = this;
+    var endpoint = this.get("type") + "/" + this.get("uuid") + "/roles";
+    var options = {
+        method: "GET",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log("could not get user roles");
+        }
+        self.roles = data.entities;
+        doCallback(callback, [ err, data, data.entities ], self);
+    });
+};
+
+Usergrid.Entity.prototype.assignRole = function(roleName, callback) {
+    var self = this;
+    var type = self.get("type");
+    var collection = type + "s";
+    var entityID;
+    if (type == "user" && this.get("username") != null) {
+        entityID = self.get("username");
+    } else if (type == "group" && this.get("name") != null) {
+        entityID = self.get("name");
+    } else if (this.get("uuid") != null) {
+        entityID = self.get("uuid");
+    }
+    if (type != "users" && type != "groups") {
+        doCallback(callback, [ new UsergridError("entity must be a group or user", "invalid_entity_type"), null, this ], this);
+    }
+    var endpoint = "roles/" + roleName + "/" + collection + "/" + entityID;
+    var options = {
+        method: "POST",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, response) {
+        if (err) {
+            console.log("Could not assign role.");
+        }
+        doCallback(callback, [ err, response, self ]);
+    });
+};
+
+Usergrid.Entity.prototype.removeRole = function(roleName, callback) {
+    var self = this;
+    var type = self.get("type");
+    var collection = type + "s";
+    var entityID;
+    if (type == "user" && this.get("username") != null) {
+        entityID = this.get("username");
+    } else if (type == "group" && this.get("name") != null) {
+        entityID = this.get("name");
+    } else if (this.get("uuid") != null) {
+        entityID = this.get("uuid");
+    }
+    if (type != "users" && type != "groups") {
+        doCallback(callback, [ new UsergridError("entity must be a group or user", "invalid_entity_type"), null, this ], this);
+    }
+    var endpoint = "roles/" + roleName + "/" + collection + "/" + entityID;
+    var options = {
+        method: "DELETE",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, response) {
+        if (err) {
+            console.log("Could not assign role.");
+        }
+        doCallback(callback, [ err, response, self ]);
+    });
+};
+
+Usergrid.Entity.prototype.assignPermissions = function(permissions, callback) {
+    var self = this;
+    var entityID;
+    var type = this.get("type");
+    if (type != "user" && type != "users" && type != "group" && type != "groups" && type != "role" && type != "roles") {
+        doCallback(callback, [ new UsergridError("entity must be a group, user, or role", "invalid_entity_type"), null, this ], this);
+    }
+    if (type == "user" && this.get("username") != null) {
+        entityID = this.get("username");
+    } else if (type == "group" && this.get("name") != null) {
+        entityID = this.get("name");
+    } else if (this.get("uuid") != null) {
+        entityID = this.get("uuid");
+    }
+    var endpoint = type + "/" + entityID + "/permissions";
+    var options = {
+        method: "POST",
+        endpoint: endpoint,
+        body: {
+            permission: permissions
+        }
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log("could not assign permissions");
+        }
+        doCallback(callback, [ err, data, data.data ], self);
+    });
+};
+
+Usergrid.Entity.prototype.removePermissions = function(permissions, callback) {
+    var self = this;
+    var entityID;
+    var type = this.get("type");
+    if (type != "user" && type != "users" && type != "group" && type != "groups" && type != "role" && type != "roles") {
+        doCallback(callback, [ new UsergridError("entity must be a group, user, or role", "invalid_entity_type"), null, this ], this);
+    }
+    if (type == "user" && this.get("username") != null) {
+        entityID = this.get("username");
+    } else if (type == "group" && this.get("name") != null) {
+        entityID = this.get("name");
+    } else if (this.get("uuid") != null) {
+        entityID = this.get("uuid");
+    }
+    var endpoint = type + "/" + entityID + "/permissions";
+    var options = {
+        method: "DELETE",
+        endpoint: endpoint,
+        qs: {
+            permission: permissions
+        }
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log("could not remove permissions");
+        }
+        doCallback(callback, [ err, data, data.params.permission ], self);
+    });
+};
+
+Usergrid.Entity.prototype.getPermissions = function(callback) {
+    var self = this;
+    var endpoint = this.get("type") + "/" + this.get("uuid") + "/permissions";
+    var options = {
+        method: "GET",
+        endpoint: endpoint
+    };
+    this._client.request(options, function(err, data) {
+        if (err && self._client.logging) {
+            console.log("could not get user permissions");
+        }
+        var permissions = [];
+        if (data.data) {
+            var perms = data.data;
+            var count = 0;
+            for (var i in perms) {
+                count++;
+                var perm = perms[i];
+                var parts = perm.split(":");
+                var ops_part = "";
+                var path_part = parts[0];
+                if (parts.length > 1) {
+                    ops_part = parts[0];
+                    path_part = parts[1];
+                }
+                ops_part = ops_part.replace("*", "get,post,put,delete");
+                var ops = ops_part.split(",");
+                var ops_object = {};
+                ops_object.get = "no";
+                ops_object.post = "no";
+                ops_object.put = "no";
+                ops_object.delete = "no";
+                for (var j in ops) {
+                    ops_object[ops[j]] = "yes";
+                }
+                permissions.push({
+                    operations: ops_object,
+                    path: path_part,
+                    perm: perm
+                });
+            }
+        }
+        self.permissions = permissions;
+        doCallback(callback, [ err, data, data.entities ], self);
+    });
+};
+
+/*
+ *  The Collection class models Usergrid Collections.  It essentially
+ *  acts as a container for holding Entity objects, while providing
+ *  additional funcitonality such as paging, and saving
+ *
+ *  @constructor
+ *  @param {string} options - configuration object
+ *  @return {Collection} collection
+ */
+Usergrid.Collection = function(options) {
+    if (options) {
+        this._client = options.client;
+        this._type = options.type;
+        this.qs = options.qs || {};
+        this._list = options.list || [];
+        this._iterator = options.iterator || -1;
+        this._previous = options.previous || [];
+        this._next = options.next || null;
+        this._cursor = options.cursor || null;
+        if (options.list) {
+            var count = options.list.length;
+            for (var i = 0; i < count; i++) {
+                var entity = this._client.restoreEntity(options.list[i]);
+                this._list[i] = entity;
+            }
+        }
+    }
+};
+
+/*
+ *  method to determine whether or not the passed variable is a Usergrid Collection
+ *
+ *  @method isCollection
+ *  @public
+ *  @params {any} obj - any variable
+ *  @return {boolean} Returns true or false
+ */
+Usergrid.isCollection = function(obj) {
+    return obj && obj instanceof Usergrid.Collection;
+};
+
+/*
+ *  gets the data from the collection object for serialization
+ *
+ *  @method serialize
+ *  @return {object} data
+ */
+Usergrid.Collection.prototype.serialize = function() {
+    var data = {};
+    data.type = this._type;
+    data.qs = this.qs;
+    data.iterator = this._iterator;
+    data.previous = this._previous;
+    data.next = this._next;
+    data.cursor = this._cursor;
+    this.resetEntityPointer();
+    var i = 0;
+    data.list = [];
+    while (this.hasNextEntity()) {
+        var entity = this.getNextEntity();
+        data.list[i] = entity.serialize();
+        i++;
+    }
+    data = JSON.stringify(data);
+    return data;
+};
+
+/*Usergrid.Collection.prototype.addCollection = function (collectionName, options, callback) {
+  self = this;
+  options.client = this._client;
+  var collection = new Usergrid.Collection(options, function(err, data) {
+    if (typeof(callback) === 'function') {
+
+      collection.resetEntityPointer();
+      while(collection.hasNextEntity()) {
+        var user = collection.getNextEntity();
+        var email = user.get('email');
+        var image = self._client.getDisplayImage(user.get('email'), user.get('picture'));
+        user._portal_image_icon = image;
+      }
+
+      self[collectionName] = collection;
+      doCallback(callback, [err, collection], self);
+    }
+  });
+};*/
+/*
+ *  Populates the collection from the server
+ *
+ *  @method fetch
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.fetch = function(callback) {
+    var self = this;
+    var qs = this.qs;
+    if (this._cursor) {
+        qs.cursor = this._cursor;
+    } else {
+        delete qs.cursor;
+    }
+    var options = {
+        method: "GET",
+        endpoint: this._type,
+        qs: this.qs
+    };
+    this._client.request(options, function(err, response) {
+        if (err && self._client.logging) {
+            console.log("error getting collection");
+        } else {
+            self.saveCursor(response.cursor || null);
+            self.resetEntityPointer();
+            self._list = response.getEntities().filter(function(entity) {
+                return isUUID(entity.uuid);
+            }).map(function(entity) {
+                var ent = new Usergrid.Entity({
+                    client: self._client
+                });
+                ent.set(entity);
+                ent.type = self._type;
+                return ent;
+            });
+        }
+        doCallback(callback, [ err, response, self ], self);
+    });
+};
+
+/*
+ *  Adds a new Entity to the collection (saves, then adds to the local object)
+ *
+ *  @method addNewEntity
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data, entity)
+ */
+Usergrid.Collection.prototype.addEntity = function(entityObject, callback) {
+    var self = this;
+    entityObject.type = this._type;
+    this._client.createEntity(entityObject, function(err, response, entity) {
+        if (!err) {
+            self.addExistingEntity(entity);
+        }
+        doCallback(callback, [ err, response, self ], self);
+    });
+};
+
+Usergrid.Collection.prototype.addExistingEntity = function(entity) {
+    var count = this._list.length;
+    this._list[count] = entity;
+};
+
+/*
+ *  Removes the Entity from the collection, then destroys the object on the server
+ *
+ *  @method destroyEntity
+ *  @param {object} entity
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.destroyEntity = function(entity, callback) {
+    var self = this;
+    entity.destroy(function(err, response) {
+        if (err) {
+            if (self._client.logging) {
+                console.log("could not destroy entity");
+            }
+            doCallback(callback, [ err, response, self ], self);
+        } else {
+            self.fetch(callback);
+        }
+        self.removeEntity(entity);
+    });
+};
+
+/*
+ * Filters the list of entities based on the supplied criteria function
+ * works like Array.prototype.filter
+ *
+ *  @method getEntitiesByCriteria
+ *  @param {function} criteria  A function that takes each entity as an argument and returns true or false
+ *  @return {Entity[]} returns a list of entities that caused the criteria function to return true
+ */
+Usergrid.Collection.prototype.getEntitiesByCriteria = function(criteria) {
+    return this._list.filter(criteria);
+};
+
+/*
+ * Returns the first entity from the list of entities based on the supplied criteria function
+ * works like Array.prototype.filter
+ *
+ *  @method getEntitiesByCriteria
+ *  @param {function} criteria  A function that takes each entity as an argument and returns true or false
+ *  @return {Entity[]} returns a list of entities that caused the criteria function to return true
+ */
+Usergrid.Collection.prototype.getEntityByCriteria = function(criteria) {
+    return this.getEntitiesByCriteria(criteria).shift();
+};
+
+/*
+ * Removed an entity from the collection without destroying it on the server
+ *
+ *  @method removeEntity
+ *  @param {object} entity
+ *  @return {Entity} returns the removed entity or undefined if it was not found
+ */
+Usergrid.Collection.prototype.removeEntity = function(entity) {
+    var removedEntity = this.getEntityByCriteria(function(item) {
+        return entity.uuid === item.get("uuid");
+    });
+    delete this._list[this._list.indexOf(removedEntity)];
+    return removedEntity;
+};
+
+/*
+ *  Looks up an Entity by UUID
+ *
+ *  @method getEntityByUUID
+ *  @param {string} UUID
+ *  @param {function} callback
+ *  @return {callback} callback(err, data, entity)
+ */
+Usergrid.Collection.prototype.getEntityByUUID = function(uuid, callback) {
+    var entity = this.getEntityByCriteria(function(item) {
+        return item.get("uuid") === uuid;
+    });
+    if (entity) {
+        doCallback(callback, [ null, entity, entity ], this);
+    } else {
+        var options = {
+            data: {
+                type: this._type,
+                uuid: uuid
+            },
+            client: this._client
+        };
+        entity = new Usergrid.Entity(options);
+        entity.fetch(callback);
+    }
+};
+
+/*
+ *  Returns the first Entity of the Entity list - does not affect the iterator
+ *
+ *  @method getFirstEntity
+ *  @return {object} returns an entity object
+ */
+Usergrid.Collection.prototype.getFirstEntity = function() {
+    var count = this._list.length;
+    if (count > 0) {
+        return this._list[0];
+    }
+    return null;
+};
+
+/*
+ *  Returns the last Entity of the Entity list - does not affect the iterator
+ *
+ *  @method getLastEntity
+ *  @return {object} returns an entity object
+ */
+Usergrid.Collection.prototype.getLastEntity = function() {
+    var count = this._list.length;
+    if (count > 0) {
+        return this._list[count - 1];
+    }
+    return null;
+};
+
+/*
+ *  Entity iteration -Checks to see if there is a "next" entity
+ *  in the list.  The first time this method is called on an entity
+ *  list, or after the resetEntityPointer method is called, it will
+ *  return true referencing the first entity in the list
+ *
+ *  @method hasNextEntity
+ *  @return {boolean} true if there is a next entity, false if not
+ */
+Usergrid.Collection.prototype.hasNextEntity = function() {
+    var next = this._iterator + 1;
+    var hasNextElement = next >= 0 && next < this._list.length;
+    if (hasNextElement) {
+        return true;
+    }
+    return false;
+};
+
+/*
+ *  Entity iteration - Gets the "next" entity in the list.  The first
+ *  time this method is called on an entity list, or after the method
+ *  resetEntityPointer is called, it will return the,
+ *  first entity in the list
+ *
+ *  @method hasNextEntity
+ *  @return {object} entity
+ */
+Usergrid.Collection.prototype.getNextEntity = function() {
+    this._iterator++;
+    var hasNextElement = this._iterator >= 0 && this._iterator <= this._list.length;
+    if (hasNextElement) {
+        return this._list[this._iterator];
+    }
+    return false;
+};
+
+/*
+ *  Entity iteration - Checks to see if there is a "previous"
+ *  entity in the list.
+ *
+ *  @method hasPrevEntity
+ *  @return {boolean} true if there is a previous entity, false if not
+ */
+Usergrid.Collection.prototype.hasPrevEntity = function() {
+    var previous = this._iterator - 1;
+    var hasPreviousElement = previous >= 0 && previous < this._list.length;
+    if (hasPreviousElement) {
+        return true;
+    }
+    return false;
+};
+
+/*
+ *  Entity iteration - Gets the "previous" entity in the list.
+ *
+ *  @method getPrevEntity
+ *  @return {object} entity
+ */
+Usergrid.Collection.prototype.getPrevEntity = function() {
+    this._iterator--;
+    var hasPreviousElement = this._iterator >= 0 && this._iterator <= this._list.length;
+    if (hasPreviousElement) {
+        return this._list[this._iterator];
+    }
+    return false;
+};
+
+/*
+ *  Entity iteration - Resets the iterator back to the beginning
+ *  of the list
+ *
+ *  @method resetEntityPointer
+ *  @return none
+ */
+Usergrid.Collection.prototype.resetEntityPointer = function() {
+    this._iterator = -1;
+};
+
+/*
+ * Method to save off the cursor just returned by the last API call
+ *
+ * @public
+ * @method saveCursor
+ * @return none
+ */
+Usergrid.Collection.prototype.saveCursor = function(cursor) {
+    if (this._next !== cursor) {
+        this._next = cursor;
+    }
+};
+
+/*
+ * Resets the paging pointer (back to original page)
+ *
+ * @public
+ * @method resetPaging
+ * @return none
+ */
+Usergrid.Collection.prototype.resetPaging = function() {
+    this._previous = [];
+    this._next = null;
+    this._cursor = null;
+};
+
+/*
+ *  Paging -  checks to see if there is a next page od data
+ *
+ *  @method hasNextPage
+ *  @return {boolean} returns true if there is a next page of data, false otherwise
+ */
+Usergrid.Collection.prototype.hasNextPage = function() {
+    return this._next;
+};
+
+/*
+ *  Paging - advances the cursor and gets the next
+ *  page of data from the API.  Stores returned entities
+ *  in the Entity list.
+ *
+ *  @method getNextPage
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.getNextPage = function(callback) {
+    if (this.hasNextPage()) {
+        this._previous.push(this._cursor);
+        this._cursor = this._next;
+        this._list = [];
+        this.fetch(callback);
+    }
+};
+
+/*
+ *  Paging -  checks to see if there is a previous page od data
+ *
+ *  @method hasPreviousPage
+ *  @return {boolean} returns true if there is a previous page of data, false otherwise
+ */
+Usergrid.Collection.prototype.hasPreviousPage = function() {
+    return this._previous.length > 0;
+};
+
+/*
+ *  Paging - reverts the cursor and gets the previous
+ *  page of data from the API.  Stores returned entities
+ *  in the Entity list.
+ *
+ *  @method getPreviousPage
+ *  @param {function} callback
+ *  @return {callback} callback(err, data)
+ */
+Usergrid.Collection.prototype.getPreviousPage = function(callback) {
+    if (this.hasPreviousPage()) {
+        this._next = null;
+        this._cursor = this._previous.pop();
+        this._list = [];
+        this.fetch(callback);
+    }
+};
+
+/*
+ *  A class to model a Usergrid group.
+ *  Set the path in the options object.
+ *
+ *  @constructor
+ *  @param {object} options {client:client, data: {'key': 'value'}, path:'path'}
+ */
+Usergrid.Group = function(options, callback) {
+    this._path = options.path;
+    this._list = [];
+    this._client = options.client;
+    this._data = options.data || {};
+    this._data.type = "groups";
+};
+
+/*
+ *  Inherit from Usergrid.Entity.
+ *  Note: This only accounts for data on the group object itself.
+ *  You need to use add and remove to manipulate group membership.
+ */
+Usergrid.Group.prototype = new Usergrid.Entity();
+
+/*
+ *  Fetches current group data, and members.
+ *
+ *  @method fetch
+ *  @public
+ *  @param {function} callback
+ *  @returns {function} callback(err, data)
+ */
+Usergrid.Group.prototype.fetch = function(callback) {
+    var self = this;
+    var groupEndpoint = "groups/" + this._path;
+    var memberEndpoint = "groups/" + this._path + "/users";
+    var groupOptions = {
+        method: "GET",
+        endpoint: groupEndpoint
+    };
+    var memberOptions = {
+        method: "GET",
+        endpoint: memberEndpoint
+    };
+    this._client.request(groupOptions, function(err, response) {
+        if (err) {
+            if (self._client.logging) {
+                console.log("error getting group");
+            }
+            doCallback(callback, [ err, response ], self);
+        } else {
+            var entities = response.getEntities();
+            if (entities && entities.length) {
+                var groupresponse = entities.shift();
+                self._client.request(memberOptions, function(err, response) {
+                    if (err && self._client.logging) {
+                        console.log("error getting group users");
+                    } else {
+                        self._list = response.getEntities().filter(function(entity) {
+                            return isUUID(entity.uuid);
+                        }).map(function(entity) {
+                            return new Usergrid.Entity({
+                                type: entity.type,
+                                client: self._client,
+                                uuid: entity.uuid,
+                                response: entity
+                            });
+                        });
+                    }
+                    doCallback(callback, [ err, response, self ], self);
+                });
+            }
+        }
+    });
+};
+
+/*
+ *  Retrieves the members of a group.
+ *
+ *  @method members
+ *  @public
+ *  @param {function} callback
+ *  @return {function} callback(err, data);
+ */
+Usergrid.Group.prototype.members = function(callback) {
+    return this._list;
+};
+
+/*
+ *  Adds an existing user to the group, and refreshes the group object.
+ *
+ *  Options object: {user: user_entity}
+ *
+ *  @method add
+ *  @public
+ *  @params {object} options
+ *  @param {function} callback
+ *  @return {function} callback(err, data)
+ */
+Usergrid.Group.prototype.add = function(options, callback) {
+    var self = this;
+    if (options.user) {
+        options = {
+            method: "POST",
+            endpoint: "groups/" + this._path + "/users/" + options.user.get("username")
+        };
+        this._client.request(options, function(error, response) {
+            if (error) {
+                doCallback(callback, [ error, response, self ], self);
+            } else {
+                self.fetch(callback);
+            }
+        });
+    } else {
+        doCallback(callback, [ new UsergridError("no user specified", "no_user_specified"), null, this ], this);
+    }
+};
+
+/*
+ *  Removes a user from a group, and refreshes the group object.
+ *
+ *  Options object: {user: user_entity}
+ *
+ *  @method remove
+ *  @public
+ *  @params {object} options
+ *  @param {function} callback
+ *  @return {function} callback(err, data)
+ */
+Usergrid.Group.prototype.remove = function(options, callback) {
+    var self = this;
+    if (options.user) {
+        options = {
+            method: "DELETE",
+            endpoint: "groups/" + this._path + "/users/" + options.user.username
+        };
+        this._client.request(options, function(error, response) {
+            if (error) {
+                doCallback(callback, [ error, response, self ], self);
+            } else {
+                self.fetch(callback);
+            }
+        });
+    } else {
+        doCallback(callback, [ new UsergridError("no user specified", "no_user_specified"), null, this ], this);
+    }
+};
+
+/*
+ * Gets feed for a group.
+ *
+ * @public
+ * @method feed
+ * @param {function} callback
+ * @returns {callback} callback(err, data, activities)
+ */
+Usergrid.Group.prototype.feed = function(callback) {
+    var self = this;
+    var options = {
+        method: "GET",
+        endpoint: "groups/" + this._path + "/feed"
+    };
+    this._client.request(options, function(err, response) {
+        doCallback(callback, [ err, response, self ], self);
+    });
+};
+
+/*
+ * Creates activity and posts to group feed.
+ *
+ * options object: {user: user_entity, content: "activity content"}
+ *
+ * @public
+ * @method createGroupActivity
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, entity)
+ */
+Usergrid.Group.prototype.createGroupActivity = function(options, callback) {
+    var self = this;
+    var user = options.user;
+    var entity = new Usergrid.Entity({
+        client: this._client,
+        data: {
+            actor: {
+                displayName: user.get("username"),
+                uuid: user.get("uuid"),
+                username: user.get("username"),
+                email: user.get("email"),
+                picture: user.get("picture"),
+                image: {
+                    duration: 0,
+                    height: 80,
+                    url: user.get("picture"),
+                    width: 80
+                }
+            },
+            verb: "post",
+            content: options.content,
+            type: "groups/" + this._path + "/activities"
+        }
+    });
+    entity.save(function(err, response, entity) {
+        doCallback(callback, [ err, response, self ]);
+    });
+};
+
+/*
+ *  A class to model a Usergrid event.
+ *
+ *  @constructor
+ *  @param {object} options {timestamp:0, category:'value', counters:{name : value}}
+ *  @returns {callback} callback(err, event)
+ */
+Usergrid.Counter = function(options) {
+    this._client = options.client;
+    this._data = options.data || {};
+    this._data.category = options.category || "UNKNOWN";
+    this._data.timestamp = options.timestamp || 0;
+    this._data.type = "events";
+    this._data.counters = options.counters || {};
+};
+
+var COUNTER_RESOLUTIONS = [ "all", "minute", "five_minutes", "half_hour", "hour", "six_day", "day", "week", "month" ];
+
+/*
+ *  Inherit from Usergrid.Entity.
+ *  Note: This only accounts for data on the group object itself.
+ *  You need to use add and remove to manipulate group membership.
+ */
+Usergrid.Counter.prototype = new Usergrid.Entity();
+
+/*
+ * overrides Entity.prototype.fetch. Returns all data for counters
+ * associated with the object as specified in the constructor
+ *
+ * @public
+ * @method increment
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Counter.prototype.fetch = function(callback) {
+    this.getData({}, callback);
+};
+
+/*
+ * increments the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method increment
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Counter.prototype.increment = function(options, callback) {
+    var self = this, name = options.name, value = options.value;
+    if (!name) {
+        return doCallback(callback, [ new UsergridInvalidArgumentError("'name' for increment, decrement must be a number"), null, self ], self);
+    } else if (isNaN(value)) {
+        return doCallback(callback, [ new UsergridInvalidArgumentError("'value' for increment, decrement must be a number"), null, self ], self);
+    } else {
+        self._data.counters[name] = parseInt(value) || 1;
+        return self.save(callback);
+    }
+};
+
+/*
+ * decrements the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method decrement
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Counter.prototype.decrement = function(options, callback) {
+    var self = this, name = options.name, value = options.value;
+    self.increment({
+        name: name,
+        value: -(parseInt(value) || 1)
+    }, callback);
+};
+
+/*
+ * resets the counter for a specific event
+ *
+ * options object: {name: counter_name}
+ *
+ * @public
+ * @method reset
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Counter.prototype.reset = function(options, callback) {
+    var self = this, name = options.name;
+    self.increment({
+        name: name,
+        value: 0
+    }, callback);
+};
+
+/*
+ * gets data for one or more counters over a given
+ * time period at a specified resolution
+ *
+ * options object: {
+ *                   counters: ['counter1', 'counter2', ...],
+ *                   start: epoch timestamp or ISO date string,
+ *                   end: epoch timestamp or ISO date string,
+ *                   resolution: one of ('all', 'minute', 'five_minutes', 'half_hour', 'hour', 'six_day', 'day', 'week', or 'month')
+ *                   }
+ *
+ * @public
+ * @method getData
+ * @params {object} options
+ * @param {function} callback
+ * @returns {callback} callback(err, event)
+ */
+Usergrid.Counter.prototype.getData = function(options, callback) {
+    var start_time, end_time, start = options.start || 0, end = options.end || Date.now(), resolution = (options.resolution || "all").toLowerCase(), counters = options.counters || Object.keys(this._data.counters), res = (resolution || "all").toLowerCase();
+    if (COUNTER_RESOLUTIONS.indexOf(res) === -1) {
+        res = "all";
+    }
+    start_time = getSafeTime(start);
+    end_time = getSafeTime(end);
+    var self = this;
+    var params = Object.keys(counters).map(function(counter) {
+        return [ "counter", encodeURIComponent(counters[counter]) ].join("=");
+    });
+    params.push("resolution=" + res);
+    params.push("start_time=" + String(start_time));
+    params.push("end_time=" + String(end_time));
+    var endpoint = "counters?" + params.join("&");
+    this._client.request({
+        endpoint: endpoint
+    }, function(err, data) {
+        if (data.counters && data.counters.length) {
+            data.counters.forEach(function(counter) {
+                self._data.counters[counter.name] = counter.value || counter.values;
+            });
+        }
+        return doCallback(callback, [ err, data, self ], self);
+    });
+};
+
+function getSafeTime(prop) {
+    var time;
+    switch (typeof prop) {
+      case "undefined":
+        time = Date.now();
+        break;
+
+      case "number":
+        time = prop;
+        break;
+
+      case "string":
+        time = isNaN(prop) ? Date.parse(prop) : parseInt(prop);
+        break;
+
+      default:
+        time = Date.parse(prop.toString());
+    }
+    return time;
+}
+
+/*
+ *  A class to model a Usergrid folder.
+ *
+ *  @constructor
+ *  @param {object} options {name:"MyPhotos", path:"/user/uploads", owner:"00000000-0000-0000-0000-000000000000" }
+ *  @returns {callback} callback(err, folder)
+ */
+Usergrid.Folder = function(options, callback) {
+    var self = this, messages = [];
+    console.log("FOLDER OPTIONS", options);
+    self._client = options.client;
+    self._data = options.data || {};
+    self._data.type = "folders";
+    var missingData = [ "name", "owner", "path" ].some(function(required) {
+        return !(required in self._data);
+    });
+    if (missingData) {
+        return doCallback(callback, [ new UsergridInvalidArgumentError("Invalid asset data: 'name', 'owner', and 'path' are required properties."), null, self ], self);
+    }
+    self.save(function(err, response) {
+        if (err) {
+            doCallback(callback, [ new UsergridError(response), response, self ], self);
+        } else {
+            if (response && response.entities && response.entities.length) {
+                self.set(response.entities[0]);
+            }
+            doCallback(callback, [ null, response, self ], self);
+        }
+    });
+};
+
+/*
+ *  Inherit from Usergrid.Entity.
+ */
+Usergrid.Folder.prototype = new Usergrid.Entity();
+
+/*
+ *  fetch the folder and associated assets
+ *
+ *  @method fetch
+ *  @public
+ *  @param {function} callback(err, self)
+ *  @returns {callback} callback(err, self)
+ */
+Usergrid.Folder.prototype.fetch = function(callback) {
+    var self = this;
+    Usergrid.Entity.prototype.fetch.call(self, function(err, data) {
+        console.log("self", self.get());
+        console.log("data", data);
+        if (!err) {
+            self.getAssets(function(err, response) {
+                if (err) {
+                    doCallback(callback, [ new UsergridError(response), resonse, self ], self);
+                } else {
+                    doCallback(callback, [ null, self ], self);
+                }
+            });
+        } else {
+            doCallback(callback, [ null, data, self ], self);
+        }
+    });
+};
+
+/*
+ *  Add an asset to the folder.
+ *
+ *  @method addAsset
+ *  @public
+ *  @param {object} options {asset:(uuid || Usergrid.Asset || {name:"photo.jpg", path:"/user/uploads", "content-type":"image/jpeg", owner:"F01DE600-0000-0000-0000-000000000000" }) }
+ *  @returns {callback} callback(err, folder)
+ */
+Usergrid.Folder.prototype.addAsset = function(options, callback) {
+    var self = this;
+    if ("asset" in options) {
+        var asset = null;
+        switch (typeof options.asset) {
+          case "object":
+            asset = options.asset;
+            if (!(asset instanceof Usergrid.Entity)) {
+                asset = new Usergrid.Asset(asset);
+            }
+            break;
+
+          case "string":
+            if (isUUID(options.asset)) {
+                asset = new Usergrid.Asset({
+                    client: self._client,
+                    data: {
+                        uuid: options.asset,
+                        type: "assets"
+                    }
+                });
+            }
+            break;
+        }
+        if (asset && asset instanceof Usergrid.Entity) {
+            asset.fetch(function(err, data) {
+                if (err) {
+                    doCallback(callback, [ new UsergridError(data), data, self ], self);
+                } else {
+                    var endpoint = [ "folders", self.get("uuid"), "assets", asset.get("uuid") ].join("/");
+                    var options = {
+                        method: "POST",
+                        endpoint: endpoint
+                    };
+                    self._client.request(options, callback);
+                }
+            });
+        }
+    } else {
+        doCallback(callback, [ new UsergridInvalidArgumentError("No asset specified"), null, self ], self);
+    }
+};
+
+/*
+ *  Remove an asset from the folder.
+ *
+ *  @method removeAsset
+ *  @public
+ *  @param {object} options {asset:(uuid || Usergrid.Asset || {name:"photo.jpg", path:"/user/uploads", "content-type":"image/jpeg", owner:"F01DE600-0000-0000-0000-000000000000" }) }
+ *  @returns {callback} callback(err, folder)
+ */
+Usergrid.Folder.prototype.removeAsset = function(options, callback) {
+    var self = this;
+    if ("asset" in options) {
+        var asset = null;
+        switch (typeof options.asset) {
+          case "object":
+            asset = options.asset;
+            break;
+
+          case "string":
+            if (isUUID(options.asset)) {
+                asset = new Usergrid.Asset({
+                    client: self._client,
+                    data: {
+                        uuid: options.asset,
+                        type: "assets"
+                    }
+                });
+            }
+            break;
+        }
+        if (asset && asset !== null) {
+            var endpoint = [ "folders", self.get("uuid"), "assets", asset.get("uuid") ].join("/");
+            self._client.request({
+                method: "DELETE",
+                endpoint: endpoint
+            }, function(err, response) {
+                if (err) {
+                    doCallback(callback, [ new UsergridError(response), response, self ], self);
+                } else {
+                    doCallback(callback, [ null, response, self ], self);
+                }
+            });
+        }
+    } else {
+        doCallback(callback, [ new UsergridInvalidArgumentError("No asset specified"), null, self ], self);
+    }
+};
+
+/*
+ *  List the assets in the folder.
+ *
+ *  @method getAssets
+ *  @public
+ *  @returns {callback} callback(err, assets)
+ */
+Usergrid.Folder.prototype.getAssets = function(callback) {
+    return this.getConnections("assets", callback);
+};
+
+/*
+ *  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) & 255;
+        }
+        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)) {
+        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);
+        }
+    });
+};
+
+/**
+ * Created by ryan bridges on 2014-02-05.
+ */
+(function(global) {
+    var name = "UsergridError", short, _name = global[name], _short = short && short !== undefined ? global[short] : undefined;
+    /*
+     *  Instantiates a new UsergridError
+     *
+     *  @method UsergridError
+     *  @public
+     *  @params {<string>} message
+     *  @params {<string>} id       - the error code, id, or name
+     *  @params {<int>} timestamp
+     *  @params {<int>} duration
+     *  @params {<string>} exception    - the Java exception from Usergrid
+     *  @return Returns - a new UsergridError object
+     *
+     *  Example:
+     *
+     *  UsergridError(message);
+     */
+    function UsergridError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name;
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridError.prototype = new Error();
+    UsergridError.prototype.constructor = UsergridError;
+    /*
+     *  Creates a UsergridError from the JSON response returned from the backend
+     *
+     *  @method fromResponse
+     *  @public
+     *  @params {object} response - the deserialized HTTP response from the Usergrid API
+     *  @return Returns a new UsergridError object.
+     *
+     *  Example:
+     *  {
+     *  "error":"organization_application_not_found",
+     *  "timestamp":1391618508079,
+     *  "duration":0,
+     *  "exception":"org.usergrid.rest.exceptions.OrganizationApplicationNotFoundException",
+     *  "error_description":"Could not find application for yourorgname/sandboxxxxx from URI: yourorgname/sandboxxxxx"
+     *  }
+     */
+    UsergridError.fromResponse = function(response) {
+        if (response && "undefined" !== typeof response) {
+            return new UsergridError(response.error_description, response.error, response.timestamp, response.duration, response.exception);
+        } else {
+            return new UsergridError();
+        }
+    };
+    UsergridError.createSubClass = function(name) {
+        if (name in global && global[name]) return global[name];
+        global[name] = function() {};
+        global[name].name = name;
+        global[name].prototype = new UsergridError();
+        return global[name];
+    };
+    function UsergridHTTPResponseError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name;
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridHTTPResponseError.prototype = new UsergridError();
+    function UsergridInvalidHTTPMethodError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name || "invalid_http_method";
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridInvalidHTTPMethodError.prototype = new UsergridError();
+    function UsergridInvalidURIError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name || "invalid_uri";
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridInvalidURIError.prototype = new UsergridError();
+    function UsergridInvalidArgumentError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name || "invalid_argument";
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridInvalidArgumentError.prototype = new UsergridError();
+    function UsergridKeystoreDatabaseUpgradeNeededError(message, name, timestamp, duration, exception) {
+        this.message = message;
+        this.name = name;
+        this.timestamp = timestamp || Date.now();
+        this.duration = duration || 0;
+        this.exception = exception;
+    }
+    UsergridKeystoreDatabaseUpgradeNeededError.prototype = new UsergridError();
+    global.UsergridHTTPResponseError = UsergridHTTPResponseError;
+    global.UsergridInvalidHTTPMethodError = UsergridInvalidHTTPMethodError;
+    global.UsergridInvalidURIError = UsergridInvalidURIError;
+    global.UsergridInvalidArgumentError = UsergridInvalidArgumentError;
+    global.UsergridKeystoreDatabaseUpgradeNeededError = UsergridKeystoreDatabaseUpgradeNeededError;
+    global[name] = UsergridError;
+    if (short !== undefined) {
+        global[short] = UsergridError;
+    }
+    global[name].noConflict = function() {
+        if (_name) {
+            global[name] = _name;
+        }
+        if (short !== undefined) {
+            global[short] = _short;
+        }
+        return UsergridError;
+    };
+    return global[name];
+})(this);
\ No newline at end of file
diff --git a/usergrid.min.js b/usergrid.min.js
new file mode 100644
index 0000000..0f50f94
--- /dev/null
+++ b/usergrid.min.js
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+function extend(subClass,superClass){var F=function(){};return F.prototype=superClass.prototype,subClass.prototype=new F,subClass.prototype.constructor=subClass,subClass.superclass=superClass.prototype,superClass.prototype.constructor==Object.prototype.constructor&&(superClass.prototype.constructor=superClass),subClass}function propCopy(from,to){for(var prop in from)from.hasOwnProperty(prop)&&(to[prop]="object"==typeof from[prop]&&"object"==typeof to[prop]?propCopy(from[prop],to[prop]):from[prop]);return to}function NOOP(){}function isValidUrl(url){if(!url)return!1;var doc,base,anchor,isValid=!1;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{return doc.head.removeChild(base),doc.body.removeChild(anchor),base=null,anchor=null,doc=null,isValid}}function isUUID(uuid){return uuid?uuidValueRegex.test(uuid):!1}function encodeParams(params){var queryString;return 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){return c instanceof Array?p.push(c):p=p.concat(Object.keys(c).map(function(key){return[key,c[key]]})),p},[]).reduce(function(p,c){return 2===c.length?p.push(c):p=p.concat(c),p},[]).reduce(function(p,c){return c[1]instanceof Array?c[1].forEach(function(v){p.push([c[0],v])}):p.push(c),p},[]).map(function(c){return c[1]=encodeURIComponent(c[1]),c.join("=")}).join("&")),queryString}function isFunction(f){return f&&null!==f&&"function"==typeof f}function doCallback(callback,params,context){var returnValue;return isFunction(callback)&&(params||(params=[]),context||(context=this),params.push(context),returnValue=callback.apply(context,params)),returnValue}function getSafeTime(prop){var time;switch(typeof prop){case"undefined":time=Date.now();break;case"number":time=prop;break;case"string":time=isNaN(prop)?Date.parse(prop):parseInt(prop);break;default:time=Date.parse(prop.toString())}return time}var UsergridEventable=function(){throw Error("'UsergridEventable' is not intended to be invoked directly")};UsergridEventable.prototype={bind:function(event,fn){this._events=this._events||{},this._events[event]=this._events[event]||[],this._events[event].push(fn)},unbind:function(event,fn){this._events=this._events||{},event in this._events!=!1&&this._events[event].splice(this._events[event].indexOf(fn),1)},trigger:function(event){if(this._events=this._events||{},event in this._events!=!1)for(var i=0;i<this._events[event].length;i++)this._events[event][i].apply(this,Array.prototype.slice.call(arguments,1))}},UsergridEventable.mixin=function(destObject){for(var props=["bind","unbind","trigger"],i=0;i<props.length;i++)props[i]in destObject.prototype&&(console.warn("overwriting '"+props[i]+"' on '"+destObject.name+"'."),console.warn("the previous version can be found at '_"+props[i]+"' on '"+destObject.name+"'."),destObject.prototype["_"+props[i]]=destObject.prototype[props[i]]),destObject.prototype[props[i]]=UsergridEventable.prototype[props[i]]},function(){function Logger(name){this.logEnabled=!0,this.init(name,!0)}var name="Logger",global=this,overwrittenName=global[name];return Logger.METHODS=["log","error","warn","info","debug","assert","clear","count","dir","dirxml","exception","group","groupCollapsed","groupEnd","profile","profileEnd","table","time","timeEnd","trace"],Logger.prototype.init=function(name,logEnabled){this.name=name||"UNKNOWN",this.logEnabled=logEnabled||!0;var addMethod=function(method){this[method]=this.createLogMethod(method)}.bind(this);Logger.METHODS.forEach(addMethod)},Logger.prototype.createLogMethod=function(method){return Logger.prototype.log.bind(this,method)},Logger.prototype.prefix=function(method,args){var prepend="["+method.toUpperCase()+"]["+name+"]:	";return-1!==["log","error","warn","info"].indexOf(method)&&("string"==typeof args[0]?args[0]=prepend+args[0]:args.unshift(prepend)),args},Logger.prototype.log=function(){var args=[].slice.call(arguments);method=args.shift(),-1===Logger.METHODS.indexOf(method)&&(method="log"),this.logEnabled&&console&&console[method]&&(args=this.prefix(method,args),console[method].apply(console,args))},Logger.prototype.setLogEnabled=function(logEnabled){this.logEnabled=logEnabled||!0},Logger.mixin=function(destObject){destObject.__logger=new Logger(destObject.name||"UNKNOWN");var addMethod=function(method){method in destObject.prototype&&(console.warn("overwriting '"+method+"' on '"+destObject.name+"'."),console.warn("the previous version can be found at '_"+method+"' on '"+destObject.name+"'."),destObject.prototype["_"+method]=destObject.prototype[method]),destObject.prototype[method]=destObject.__logger.createLogMethod(method)};Logger.METHODS.forEach(addMethod)},global[name]=Logger,global[name].noConflict=function(){return overwrittenName&&(global[name]=overwrittenName),Logger},global[name]}(),function(global){function Promise(){this.complete=!1,this.error=null,this.result=null,this.callbacks=[]}var name="Promise",overwrittenName=global[name];return Promise.prototype.then=function(callback,context){var f=function(){return callback.apply(context,arguments)};this.complete?f(this.error,this.result):this.callbacks.push(f)},Promise.prototype.done=function(error,result){if(this.complete=!0,this.error=error,this.result=result,this.callbacks){for(var i=0;i<this.callbacks.length;i++)this.callbacks[i](error,result);this.callbacks.length=0}},Promise.join=function(promises){function notifier(i){return function(error,result){completed+=1,errors[i]=error,results[i]=result,completed===total&&p.done(errors,results)}}for(var p=new Promise,total=promises.length,completed=0,errors=[],results=[],i=0;total>i;i++)promises[i]().then(notifier(i));return p},Promise.chain=function(promises,error,result){var p=new Promise;return null===promises||0===promises.length?p.done(error,result):promises[0](error,result).then(function(res,err){promises.splice(0,1),promises?Promise.chain(promises,res,err).then(function(r,e){p.done(r,e)}):p.done(res,err)}),p},global[name]=Promise,global[name].noConflict=function(){return overwrittenName&&(global[name]=overwrittenName),Promise},global[name]}(this),function(){function partial(){var args=Array.prototype.slice.call(arguments),fn=args.shift();return fn.bind(this,args)}function Ajax(){function encode(data){var result="";if("string"==typeof data)result=data;else{var e=encodeURIComponent;for(var i in data)data.hasOwnProperty(i)&&(result+="&"+e(i)+"="+e(data[i]))}return result}function request(m,u,d){var timeout,p=new Promise;return self.logger.time(m+" "+u),function(xhr){xhr.onreadystatechange=function(){4===this.readyState&&(self.logger.timeEnd(m+" "+u),clearTimeout(timeout),p.done(null,this))},xhr.onerror=function(response){clearTimeout(timeout),p.done(response,null)},xhr.oncomplete=function(){clearTimeout(timeout),self.logger.timeEnd(m+" "+u),self.info("%s request to %s returned %s",m,u,this.status)},xhr.open(m,u),d&&("object"==typeof d&&(d=JSON.stringify(d)),xhr.setRequestHeader("Content-Type","application/json"),xhr.setRequestHeader("Accept","application/json")),timeout=setTimeout(function(){xhr.abort(),p.done("API Call timed out.",null)},3e4),xhr.send(encode(d))}(new XMLHttpRequest),p}this.logger=new global.Logger(name);var self=this;this.request=request,this.get=partial(request,"GET"),this.post=partial(request,"POST"),this.put=partial(request,"PUT"),this.delete=partial(request,"DELETE")}var exports,name="Ajax",global=this,overwrittenName=global[name];return global[name]=new Ajax,global[name].noConflict=function(){return overwrittenName&&(global[name]=overwrittenName),exports},global[name]}(),window.console=window.console||{},window.console.log=window.console.log||function(){};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(global){function Usergrid(){this.logger=new Logger(name)}var name="Usergrid",overwrittenName=global[name],VALID_REQUEST_METHODS=["GET","POST","PUT","DELETE"];return Usergrid.isValidEndpoint=function(){return!0},Usergrid.Request=function(method,endpoint,query_params,data,callback){var p=new Promise;if(this.logger=new global.Logger("Usergrid.Request"),this.logger.time("process request "+method+" "+endpoint),this.endpoint=endpoint+"?"+encodeParams(query_params),this.method=method.toUpperCase(),this.data="object"==typeof data?JSON.stringify(data):data,-1===VALID_REQUEST_METHODS.indexOf(this.method))throw new UsergridInvalidHTTPMethodError("invalid request method '"+this.method+"'");if(!isValidUrl(this.endpoint))throw this.logger.error(endpoint,this.endpoint,/^https:\/\//.test(endpoint)),new UsergridInvalidURIError("The provided endpoint is not valid: "+this.endpoint);var request=function(){return Ajax.request(this.method,this.endpoint,this.data)}.bind(this),response=function(err,request){return new Usergrid.Response(err,request)}.bind(this),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);return Promise.chain([request,response]).then(oncomplete),p},Usergrid.Response=function(err,response){var p=new Promise,data=null;try{data=JSON.parse(response.responseText)}catch(e){data={}}switch(Object.keys(data).forEach(function(key){Object.defineProperty(this,key,{value:data[key],enumerable:!0})}.bind(this)),Object.defineProperty(this,"logger",{enumerable:!1,configurable:!1,writable:!1,value:new global.Logger(name)}),Object.defineProperty(this,"success",{enumerable:!1,configurable:!1,writable:!0,value:!0}),Object.defineProperty(this,"err",{enumerable:!1,configurable:!1,writable:!0,value:err}),Object.defineProperty(this,"status",{enumerable:!1,configurable:!1,writable:!0,value:parseInt(response.status)}),Object.defineProperty(this,"statusGroup",{enumerable:!1,configurable:!1,writable:!0,value:this.status-this.status%100}),this.statusGroup){case 200:this.success=!0;break;case 400:case 500:case 300:case 100:default:this.success=!1}return this.success?p.done(null,this):p.done(UsergridError.fromResponse(data),this),p},Usergrid.Response.prototype.getEntities=function(){var entities;return this.success&&(entities=this.data?this.data.entities:this.entities),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(){return overwrittenName&&(global[name]=overwrittenName),Usergrid},global[name]}(this),function(){var exports,name="Client",global=this,overwrittenName=global[name];return Usergrid.Client=function(options){this.URI=options.URI||"https://api.usergrid.com",options.orgName&&this.set("orgName",options.orgName),options.appName&&this.set("appName",options.appName),options.qs&&this.setObject("default_qs",options.qs),this.buildCurl=options.buildCurl||!1,this.logging=options.logging||!1},Usergrid.Client.prototype.request=function(options,callback){var uri,method=options.method||"GET",endpoint=options.endpoint,body=options.body||{},qs=options.qs||{},mQuery=options.mQuery||!1,orgName=this.get("orgName"),appName=this.get("appName"),default_qs=this.getObject("default_qs");if(!mQuery&&!orgName&&!appName)return logoutCallback();uri=mQuery?this.URI+"/"+endpoint:this.URI+"/"+orgName+"/"+appName+"/"+endpoint,this.getToken()&&(qs.access_token=this.getToken()),default_qs&&(qs=propCopy(qs,default_qs));{var self=this;new Usergrid.Request(method,uri,qs,body,function(err,response){err?doCallback(callback,[err,response,self],self):doCallback(callback,[null,response,self],self)})}},Usergrid.Client.prototype.buildAssetURL=function(uuid){var self=this,qs={},assetURL=this.URI+"/"+this.orgName+"/"+this.appName+"/assets/"+uuid+"/data";self.getToken()&&(qs.access_token=self.getToken());var encoded_params=encodeParams(qs);return encoded_params&&(assetURL+="?"+encoded_params),assetURL},Usergrid.Client.prototype.createGroup=function(options,callback){var group=new Usergrid.Group({path:options.path,client:this,data:options});group.save(function(err,response){doCallback(callback,[err,response,group],group)})},Usergrid.Client.prototype.createEntity=function(options,callback){var entity=new Usergrid.Entity({client:this,data:options});entity.save(function(err,response){doCallback(callback,[err,response,entity],entity)})},Usergrid.Client.prototype.getEntity=function(options,callback){var entity=new Usergrid.Entity({client:this,data:options});entity.fetch(function(err,response){doCallback(callback,[err,response,entity],entity)})},Usergrid.Client.prototype.restoreEntity=function(serializedObject){var data=JSON.parse(serializedObject),options={client:this,data:data},entity=new Usergrid.Entity(options);return entity},Usergrid.Client.prototype.createCounter=function(options,callback){var counter=new Usergrid.Counter({client:this,data:options});counter.save(callback)},Usergrid.Client.prototype.createAsset=function(options,callback){var file=options.file;file&&(options.name=options.name||file.name,options["content-type"]=options["content-type"]||file.type,options.path=options.path||"/",delete options.file);var asset=new Usergrid.Asset({client:this,data:options});asset.save(function(err,response,asset){file&&!err?asset.upload(file,callback):doCallback(callback,[err,response,asset],asset)})},Usergrid.Client.prototype.createCollection=function(options,callback){return options.client=this,new Usergrid.Collection(options,function(err,data,collection){console.log("createCollection",arguments),doCallback(callback,[err,collection,data])})},Usergrid.Client.prototype.restoreCollection=function(serializedObject){var data=JSON.parse(serializedObject);data.client=this;var collection=new Usergrid.Collection(data);return collection},Usergrid.Client.prototype.getFeedForUser=function(username,callback){var options={method:"GET",endpoint:"users/"+username+"/feed"};this.request(options,function(err,data){err?doCallback(callback,[err]):doCallback(callback,[err,data,data.getEntities()])})},Usergrid.Client.prototype.createUserActivity=function(user,options,callback){options.type="users/"+user+"/activities",options={client:this,data:options};var entity=new Usergrid.Entity(options);entity.save(function(err,data){doCallback(callback,[err,data,entity])})},Usergrid.Client.prototype.createUserActivityWithEntity=function(user,content,callback){var username=user.get("username"),options={actor:{displayName:username,uuid:user.get("uuid"),username:username,email:user.get("email"),picture:user.get("picture"),image:{duration:0,height:80,url:user.get("picture"),width:80}},verb:"post",content:content};this.createUserActivity(username,options,callback)},Usergrid.Client.prototype.calcTimeDiff=function(){var seconds=0,time=this._end-this._start;try{seconds=(time/10/60).toFixed(2)}catch(e){return 0}return seconds},Usergrid.Client.prototype.setToken=function(token){this.set("token",token)},Usergrid.Client.prototype.getToken=function(){return this.get("token")},Usergrid.Client.prototype.setObject=function(key,value){value&&(value=JSON.stringify(value)),this.set(key,value)},Usergrid.Client.prototype.set=function(key,value){var keyStore="apigee_"+key;this[key]=value,"undefined"!=typeof Storage&&(value?localStorage.setItem(keyStore,value):localStorage.removeItem(keyStore))},Usergrid.Client.prototype.getObject=function(key){return JSON.parse(this.get(key))},Usergrid.Client.prototype.get=function(key){var keyStore="apigee_"+key,value=null;return this[key]?value=this[key]:"undefined"!=typeof Storage&&(value=localStorage.getItem(keyStore)),value},Usergrid.Client.prototype.signup=function(username,password,email,name,callback){var options={type:"users",username:username,password:password,email:email,name:name};this.createEntity(options,callback)},Usergrid.Client.prototype.login=function(username,password,callback){var self=this,options={method:"POST",endpoint:"token",body:{username:username,password:password,grant_type:"password"}};self.request(options,function(err,response){var user={};if(err)self.logging&&console.log("error trying to log user in");else{var options={client:self,data:response.user};user=new Usergrid.Entity(options),self.setToken(response.access_token)}doCallback(callback,[err,response,user])})},Usergrid.Client.prototype.reAuthenticateLite=function(callback){var self=this,options={method:"GET",endpoint:"management/me",mQuery:!0};this.request(options,function(err,response){err&&self.logging?console.log("error trying to re-authenticate user"):self.setToken(response.data.access_token),doCallback(callback,[err])})},Usergrid.Client.prototype.reAuthenticate=function(email,callback){var self=this,options={method:"GET",endpoint:"management/users/"+email,mQuery:!0};this.request(options,function(err,response){var data,organizations={},applications={},user={};if(err&&self.logging)console.log("error trying to full authenticate user");else{data=response.data,self.setToken(data.token),self.set("email",data.email),localStorage.setItem("accessToken",data.token),localStorage.setItem("userUUID",data.uuid),localStorage.setItem("userEmail",data.email);var userData={username:data.username,email:data.email,name:data.name,uuid:data.uuid},options={client:self,data:userData};user=new Usergrid.Entity(options),organizations=data.organizations;var org="";try{var existingOrg=self.get("orgName");org=organizations[existingOrg]?organizations[existingOrg]:organizations[Object.keys(organizations)[0]],self.set("orgName",org.name)}catch(e){err=!0,self.logging&&console.log("error selecting org")}applications=self.parseApplicationsArray(org),self.selectFirstApp(applications),self.setObject("organizations",organizations),self.setObject("applications",applications)}doCallback(callback,[err,data,user,organizations,applications],self)})},Usergrid.Client.prototype.loginFacebook=function(facebookToken,callback){var self=this,options={method:"GET",endpoint:"auth/facebook",qs:{fb_access_token:facebookToken}};this.request(options,function(err,data){var user={};if(err&&self.logging)console.log("error trying to log user in");else{var options={client:self,data:data.user};user=new Usergrid.Entity(options),self.setToken(data.access_token)}doCallback(callback,[err,data,user],self)})},Usergrid.Client.prototype.getLoggedInUser=function(callback){var self=this;if(this.getToken()){var options={method:"GET",endpoint:"users/me"};this.request(options,function(err,response){if(err)self.logging&&console.log("error trying to log user in"),console.error(err,response),doCallback(callback,[err,response,self],self);else{var options={client:self,data:response.getEntity()},user=new Usergrid.Entity(options);doCallback(callback,[null,response,user],self)}})}else doCallback(callback,[new UsergridError("Access Token not set"),null,self],self)},Usergrid.Client.prototype.isLoggedIn=function(){var token=this.getToken();return"undefined"!=typeof token&&null!==token},Usergrid.Client.prototype.logout=function(){this.setToken()},Usergrid.Client.prototype.destroyToken=function(username,token,revokeAll,callback){var options={client:self,method:"PUT"};options.endpoint=revokeAll===!0?"users/"+username+"/revoketokens":null===token?"users/"+username+"/revoketoken?token="+this.getToken():"users/"+username+"/revoketoken?token="+token,this.request(options,function(err,data){err?(self.logging&&console.log("error destroying access token"),doCallback(callback,[err,data,null],self)):(console.log(revokeAll===!0?"all user tokens invalidated":"token invalidated"),doCallback(callback,[err,data,null],self))})},Usergrid.Client.prototype.logoutAndDestroyToken=function(username,token,revokeAll,callback){null===username?console.log("username required to revoke tokens"):(this.destroyToken(username,token,revokeAll,callback),(revokeAll===!0||token===this.getToken()||null===token)&&this.setToken(null))},Usergrid.Client.prototype.buildCurlCall=function(options){var curl=["curl"],method=(options.method||"GET").toUpperCase(),body=options.body,uri=options.uri;return curl.push("-X"),curl.push(["POST","PUT","DELETE"].indexOf(method)>=0?method:"GET"),curl.push(uri),"object"==typeof body&&Object.keys(body).length>0&&-1!==["POST","PUT"].indexOf(method)&&(curl.push("-d"),curl.push("'"+JSON.stringify(body)+"'")),curl=curl.join(" "),console.log(curl),curl},Usergrid.Client.prototype.getDisplayImage=function(email,picture,size){size=size||50;var image="https://apigee.com/usergrid/images/user_profile.png";try{picture?image=picture:email.length&&(image="https://secure.gravatar.com/avatar/"+MD5(email)+"?s="+size+encodeURI("&d=https://apigee.com/usergrid/images/user_profile.png"))}catch(e){}finally{return image}},global[name]=Usergrid.Client,global[name].noConflict=function(){return overwrittenName&&(global[name]=overwrittenName),exports},global[name]}();var ENTITY_SYSTEM_PROPERTIES=["metadata","created","modified","oldpassword","newpassword","type","activated","uuid"];Usergrid.Entity=function(options){this._data={},this._client=void 0,options&&(this.set(options.data||{}),this._client=options.client||{})},Usergrid.Entity.isEntity=function(obj){return obj&&obj instanceof Usergrid.Entity},Usergrid.Entity.isPersistedEntity=function(obj){return isEntity(obj)&&isUUID(obj.get("uuid"))},Usergrid.Entity.prototype.serialize=function(){return JSON.stringify(this._data)},Usergrid.Entity.prototype.get=function(key){var value;if(0===arguments.length?value=this._data:arguments.length>1&&(key=[].slice.call(arguments).reduce(function(p,c){return c instanceof Array?p=p.concat(c):p.push(c),p},[])),key instanceof Array){var self=this;value=key.map(function(k){return self.get(k)})}else"undefined"!=typeof key&&(value=this._data[key]);return value},Usergrid.Entity.prototype.set=function(key,value){if("object"==typeof key)for(var field in key)this._data[field]=key[field];else"string"==typeof key?null===value?delete this._data[key]:this._data[key]=value:this._data={}},Usergrid.Entity.prototype.getEndpoint=function(){var name,type=this.get("type"),nameProperties=["uuid","name"];if(void 0===type)throw new UsergridError("cannot fetch entity, no entity type specified","no_type_specified");return/^users?$/.test(type)&&nameProperties.unshift("username"),name=this.get(nameProperties).filter(function(x){return null!==x&&"undefined"!=typeof x}).shift(),name?[type,name].join("/"):type},Usergrid.Entity.prototype.save=function(callback){var self=this,type=this.get("type"),method="POST",entityId=this.get("uuid"),entityData=this.get(),options={method:method,endpoint:type};entityId&&(options.method="PUT",options.endpoint+="/"+entityId),options.body=Object.keys(entityData).filter(function(key){return-1===ENTITY_SYSTEM_PROPERTIES.indexOf(key)}).reduce(function(data,key){return data[key]=entityData[key],data},{}),self._client.request(options,function(err,response){var entity=response.getEntity();entity&&(self.set(entity),self.set("type",/^\//.test(response.path)?response.path.substring(1):response.path)),err&&self._client.logging&&console.log("could not save entity"),doCallback(callback,[err,response,self],self)})},Usergrid.Entity.prototype.changePassword=function(oldpassword,newpassword,callback){var self=this;if("function"==typeof oldpassword&&void 0===callback&&(callback=oldpassword,oldpassword=self.get("oldpassword"),newpassword=self.get("newpassword")),self.set({password:null,oldpassword:null,newpassword:null}),!(/^users?$/.test(self.get("type"))&&oldpassword&&newpassword))throw new UsergridInvalidArgumentError("Invalid arguments passed to 'changePassword'");var options={method:"PUT",endpoint:"users/"+self.get("uuid")+"/password",body:{uuid:self.get("uuid"),username:self.get("username"),oldpassword:oldpassword,newpassword:newpassword}};self._client.request(options,function(err,response){err&&self._client.logging&&console.log("could not update user"),doCallback(callback,[err,response,self],self)})},Usergrid.Entity.prototype.fetch=function(callback){var endpoint,self=this;endpoint=this.getEndpoint();var options={method:"GET",endpoint:endpoint};this._client.request(options,function(err,response){var entity=response.getEntity();entity&&self.set(entity),doCallback(callback,[err,response,self],self)})},Usergrid.Entity.prototype.destroy=function(callback){var self=this,endpoint=this.getEndpoint(),options={method:"DELETE",endpoint:endpoint};this._client.request(options,function(err,response){err||self.set(null),doCallback(callback,[err,response,self],self)})},Usergrid.Entity.prototype.connect=function(connection,entity,callback){this.addOrRemoveConnection("POST",connection,entity,callback)},Usergrid.Entity.prototype.disconnect=function(connection,entity,callback){this.addOrRemoveConnection("DELETE",connection,entity,callback)},Usergrid.Entity.prototype.addOrRemoveConnection=function(method,connection,entity,callback){var self=this;if(-1==["POST","DELETE"].indexOf(method.toUpperCase()))throw new UsergridInvalidArgumentError("invalid method for connection call. must be 'POST' or 'DELETE'");var connecteeType=entity.get("type"),connectee=this.getEntityId(entity);if(!connectee)throw new UsergridInvalidArgumentError("connectee could not be identified");var connectorType=this.get("type"),connector=this.getEntityId(this);if(!connector)throw new UsergridInvalidArgumentError("connector could not be identified");var endpoint=[connectorType,connector,connection,connecteeType,connectee].join("/"),options={method:method,endpoint:endpoint};this._client.request(options,function(err,response){err&&self._client.logging&&console.log("There was an error with the connection call"),doCallback(callback,[err,response,self],self)})},Usergrid.Entity.prototype.getEntityId=function(entity){var id;return id=entity.get(isUUID(entity.get("uuid"))?"uuid":"users"===this.get("type")||"user"===this.get("type")?"username":"name")},Usergrid.Entity.prototype.getConnections=function(connection,callback){var self=this,connectorType=this.get("type"),connector=this.getEntityId(this);if(connector){var endpoint=connectorType+"/"+connector+"/"+connection+"/",options={method:"GET",endpoint:endpoint};this._client.request(options,function(err,data){err&&self._client.logging&&console.log("entity could not be connected"),self[connection]={};for(var length=data&&data.entities?data.entities.length:0,i=0;length>i;i++)"user"===data.entities[i].type?self[connection][data.entities[i].username]=data.entities[i]:self[connection][data.entities[i].name]=data.entities[i];doCallback(callback,[err,data,data.entities],self)})}else if("function"==typeof callback){var error="Error in getConnections - no uuid specified.";self._client.logging&&console.log(error),doCallback(callback,[!0,error],self)}},Usergrid.Entity.prototype.getGroups=function(callback){var self=this,endpoint="users/"+this.get("uuid")+"/groups",options={method:"GET",endpoint:endpoint};this._client.request(options,function(err,data){err&&self._client.logging&&console.log("entity could not be connected"),self.groups=data.entities,doCallback(callback,[err,data,data.entities],self)})},Usergrid.Entity.prototype.getActivities=function(callback){var self=this,endpoint=this.get("type")+"/"+this.get("uuid")+"/activities",options={method:"GET",endpoint:endpoint};this._client.request(options,function(err,data){err&&self._client.logging&&console.log("entity could not be connected");for(var entity in data.entities)data.entities[entity].createdDate=new Date(data.entities[entity].created).toUTCString();self.activities=data.entities,doCallback(callback,[err,data,data.entities],self)})},Usergrid.Entity.prototype.getFollowing=function(callback){var self=this,endpoint="users/"+this.get("uuid")+"/following",options={method:"GET",endpoint:endpoint};this._client.request(options,function(err,data){err&&self._client.logging&&console.log("could not get user following");for(var entity in data.entities){data.entities[entity].createdDate=new Date(data.entities[entity].created).toUTCString();var image=self._client.getDisplayImage(data.entities[entity].email,data.entities[entity].picture);data.entities[entity]._portal_image_icon=image}self.following=data.entities,doCallback(callback,[err,data,data.entities],self)})},Usergrid.Entity.prototype.getFollowers=function(callback){var self=this,endpoint="users/"+this.get("uuid")+"/followers",options={method:"GET",endpoint:endpoint};this._client.request(options,function(err,data){err&&self._client.logging&&console.log("could not get user followers");for(var entity in data.entities){data.entities[entity].createdDate=new Date(data.entities[entity].created).toUTCString();var image=self._client.getDisplayImage(data.entities[entity].email,data.entities[entity].picture);data.entities[entity]._portal_image_icon=image}self.followers=data.entities,doCallback(callback,[err,data,data.entities],self)})},Usergrid.Client.prototype.createRole=function(roleName,permissions,callback){var options={type:"role",name:roleName};this.createEntity(options,function(err,response,entity){err?doCallback(callback,[err,response,self]):entity.assignPermissions(permissions,function(err,data){err?doCallback(callback,[err,response,self]):doCallback(callback,[err,data,data.data],self)})})},Usergrid.Entity.prototype.getRoles=function(callback){var self=this,endpoint=this.get("type")+"/"+this.get("uuid")+"/roles",options={method:"GET",endpoint:endpoint};this._client.request(options,function(err,data){err&&self._client.logging&&console.log("could not get user roles"),self.roles=data.entities,doCallback(callback,[err,data,data.entities],self)})},Usergrid.Entity.prototype.assignRole=function(roleName,callback){var entityID,self=this,type=self.get("type"),collection=type+"s";"user"==type&&null!=this.get("username")?entityID=self.get("username"):"group"==type&&null!=this.get("name")?entityID=self.get("name"):null!=this.get("uuid")&&(entityID=self.get("uuid")),"users"!=type&&"groups"!=type&&doCallback(callback,[new UsergridError("entity must be a group or user","invalid_entity_type"),null,this],this);var endpoint="roles/"+roleName+"/"+collection+"/"+entityID,options={method:"POST",endpoint:endpoint};this._client.request(options,function(err,response){err&&console.log("Could not assign role."),doCallback(callback,[err,response,self])})},Usergrid.Entity.prototype.removeRole=function(roleName,callback){var entityID,self=this,type=self.get("type"),collection=type+"s";"user"==type&&null!=this.get("username")?entityID=this.get("username"):"group"==type&&null!=this.get("name")?entityID=this.get("name"):null!=this.get("uuid")&&(entityID=this.get("uuid")),"users"!=type&&"groups"!=type&&doCallback(callback,[new UsergridError("entity must be a group or user","invalid_entity_type"),null,this],this);var endpoint="roles/"+roleName+"/"+collection+"/"+entityID,options={method:"DELETE",endpoint:endpoint};this._client.request(options,function(err,response){err&&console.log("Could not assign role."),doCallback(callback,[err,response,self])})},Usergrid.Entity.prototype.assignPermissions=function(permissions,callback){var entityID,self=this,type=this.get("type");"user"!=type&&"users"!=type&&"group"!=type&&"groups"!=type&&"role"!=type&&"roles"!=type&&doCallback(callback,[new UsergridError("entity must be a group, user, or role","invalid_entity_type"),null,this],this),"user"==type&&null!=this.get("username")?entityID=this.get("username"):"group"==type&&null!=this.get("name")?entityID=this.get("name"):null!=this.get("uuid")&&(entityID=this.get("uuid"));var endpoint=type+"/"+entityID+"/permissions",options={method:"POST",endpoint:endpoint,body:{permission:permissions}};this._client.request(options,function(err,data){err&&self._client.logging&&console.log("could not assign permissions"),doCallback(callback,[err,data,data.data],self)})},Usergrid.Entity.prototype.removePermissions=function(permissions,callback){var entityID,self=this,type=this.get("type");
+"user"!=type&&"users"!=type&&"group"!=type&&"groups"!=type&&"role"!=type&&"roles"!=type&&doCallback(callback,[new UsergridError("entity must be a group, user, or role","invalid_entity_type"),null,this],this),"user"==type&&null!=this.get("username")?entityID=this.get("username"):"group"==type&&null!=this.get("name")?entityID=this.get("name"):null!=this.get("uuid")&&(entityID=this.get("uuid"));var endpoint=type+"/"+entityID+"/permissions",options={method:"DELETE",endpoint:endpoint,qs:{permission:permissions}};this._client.request(options,function(err,data){err&&self._client.logging&&console.log("could not remove permissions"),doCallback(callback,[err,data,data.params.permission],self)})},Usergrid.Entity.prototype.getPermissions=function(callback){var self=this,endpoint=this.get("type")+"/"+this.get("uuid")+"/permissions",options={method:"GET",endpoint:endpoint};this._client.request(options,function(err,data){err&&self._client.logging&&console.log("could not get user permissions");var permissions=[];if(data.data){var perms=data.data,count=0;for(var i in perms){count++;var perm=perms[i],parts=perm.split(":"),ops_part="",path_part=parts[0];parts.length>1&&(ops_part=parts[0],path_part=parts[1]),ops_part=ops_part.replace("*","get,post,put,delete");var ops=ops_part.split(","),ops_object={};ops_object.get="no",ops_object.post="no",ops_object.put="no",ops_object.delete="no";for(var j in ops)ops_object[ops[j]]="yes";permissions.push({operations:ops_object,path:path_part,perm:perm})}}self.permissions=permissions,doCallback(callback,[err,data,data.entities],self)})},Usergrid.Collection=function(options){if(options&&(this._client=options.client,this._type=options.type,this.qs=options.qs||{},this._list=options.list||[],this._iterator=options.iterator||-1,this._previous=options.previous||[],this._next=options.next||null,this._cursor=options.cursor||null,options.list))for(var count=options.list.length,i=0;count>i;i++){var entity=this._client.restoreEntity(options.list[i]);this._list[i]=entity}},Usergrid.isCollection=function(obj){return obj&&obj instanceof Usergrid.Collection},Usergrid.Collection.prototype.serialize=function(){var data={};data.type=this._type,data.qs=this.qs,data.iterator=this._iterator,data.previous=this._previous,data.next=this._next,data.cursor=this._cursor,this.resetEntityPointer();var i=0;for(data.list=[];this.hasNextEntity();){var entity=this.getNextEntity();data.list[i]=entity.serialize(),i++}return data=JSON.stringify(data)},Usergrid.Collection.prototype.fetch=function(callback){var self=this,qs=this.qs;this._cursor?qs.cursor=this._cursor:delete qs.cursor;var options={method:"GET",endpoint:this._type,qs:this.qs};this._client.request(options,function(err,response){err&&self._client.logging?console.log("error getting collection"):(self.saveCursor(response.cursor||null),self.resetEntityPointer(),self._list=response.getEntities().filter(function(entity){return isUUID(entity.uuid)}).map(function(entity){var ent=new Usergrid.Entity({client:self._client});return ent.set(entity),ent.type=self._type,ent})),doCallback(callback,[err,response,self],self)})},Usergrid.Collection.prototype.addEntity=function(entityObject,callback){var self=this;entityObject.type=this._type,this._client.createEntity(entityObject,function(err,response,entity){err||self.addExistingEntity(entity),doCallback(callback,[err,response,self],self)})},Usergrid.Collection.prototype.addExistingEntity=function(entity){var count=this._list.length;this._list[count]=entity},Usergrid.Collection.prototype.destroyEntity=function(entity,callback){var self=this;entity.destroy(function(err,response){err?(self._client.logging&&console.log("could not destroy entity"),doCallback(callback,[err,response,self],self)):self.fetch(callback),self.removeEntity(entity)})},Usergrid.Collection.prototype.getEntitiesByCriteria=function(criteria){return this._list.filter(criteria)},Usergrid.Collection.prototype.getEntityByCriteria=function(criteria){return this.getEntitiesByCriteria(criteria).shift()},Usergrid.Collection.prototype.removeEntity=function(entity){var removedEntity=this.getEntityByCriteria(function(item){return entity.uuid===item.get("uuid")});return delete this._list[this._list.indexOf(removedEntity)],removedEntity},Usergrid.Collection.prototype.getEntityByUUID=function(uuid,callback){var entity=this.getEntityByCriteria(function(item){return item.get("uuid")===uuid});if(entity)doCallback(callback,[null,entity,entity],this);else{var options={data:{type:this._type,uuid:uuid},client:this._client};entity=new Usergrid.Entity(options),entity.fetch(callback)}},Usergrid.Collection.prototype.getFirstEntity=function(){var count=this._list.length;return count>0?this._list[0]:null},Usergrid.Collection.prototype.getLastEntity=function(){var count=this._list.length;return count>0?this._list[count-1]:null},Usergrid.Collection.prototype.hasNextEntity=function(){var next=this._iterator+1,hasNextElement=next>=0&&next<this._list.length;return hasNextElement?!0:!1},Usergrid.Collection.prototype.getNextEntity=function(){this._iterator++;var hasNextElement=this._iterator>=0&&this._iterator<=this._list.length;return hasNextElement?this._list[this._iterator]:!1},Usergrid.Collection.prototype.hasPrevEntity=function(){var previous=this._iterator-1,hasPreviousElement=previous>=0&&previous<this._list.length;return hasPreviousElement?!0:!1},Usergrid.Collection.prototype.getPrevEntity=function(){this._iterator--;var hasPreviousElement=this._iterator>=0&&this._iterator<=this._list.length;return hasPreviousElement?this._list[this._iterator]:!1},Usergrid.Collection.prototype.resetEntityPointer=function(){this._iterator=-1},Usergrid.Collection.prototype.saveCursor=function(cursor){this._next!==cursor&&(this._next=cursor)},Usergrid.Collection.prototype.resetPaging=function(){this._previous=[],this._next=null,this._cursor=null},Usergrid.Collection.prototype.hasNextPage=function(){return this._next},Usergrid.Collection.prototype.getNextPage=function(callback){this.hasNextPage()&&(this._previous.push(this._cursor),this._cursor=this._next,this._list=[],this.fetch(callback))},Usergrid.Collection.prototype.hasPreviousPage=function(){return this._previous.length>0},Usergrid.Collection.prototype.getPreviousPage=function(callback){this.hasPreviousPage()&&(this._next=null,this._cursor=this._previous.pop(),this._list=[],this.fetch(callback))},Usergrid.Group=function(options){this._path=options.path,this._list=[],this._client=options.client,this._data=options.data||{},this._data.type="groups"},Usergrid.Group.prototype=new Usergrid.Entity,Usergrid.Group.prototype.fetch=function(callback){var self=this,groupEndpoint="groups/"+this._path,memberEndpoint="groups/"+this._path+"/users",groupOptions={method:"GET",endpoint:groupEndpoint},memberOptions={method:"GET",endpoint:memberEndpoint};this._client.request(groupOptions,function(err,response){if(err)self._client.logging&&console.log("error getting group"),doCallback(callback,[err,response],self);else{var entities=response.getEntities();if(entities&&entities.length){{entities.shift()}self._client.request(memberOptions,function(err,response){err&&self._client.logging?console.log("error getting group users"):self._list=response.getEntities().filter(function(entity){return isUUID(entity.uuid)}).map(function(entity){return new Usergrid.Entity({type:entity.type,client:self._client,uuid:entity.uuid,response:entity})}),doCallback(callback,[err,response,self],self)})}}})},Usergrid.Group.prototype.members=function(){return this._list},Usergrid.Group.prototype.add=function(options,callback){var self=this;options.user?(options={method:"POST",endpoint:"groups/"+this._path+"/users/"+options.user.get("username")},this._client.request(options,function(error,response){error?doCallback(callback,[error,response,self],self):self.fetch(callback)})):doCallback(callback,[new UsergridError("no user specified","no_user_specified"),null,this],this)},Usergrid.Group.prototype.remove=function(options,callback){var self=this;options.user?(options={method:"DELETE",endpoint:"groups/"+this._path+"/users/"+options.user.username},this._client.request(options,function(error,response){error?doCallback(callback,[error,response,self],self):self.fetch(callback)})):doCallback(callback,[new UsergridError("no user specified","no_user_specified"),null,this],this)},Usergrid.Group.prototype.feed=function(callback){var self=this,options={method:"GET",endpoint:"groups/"+this._path+"/feed"};this._client.request(options,function(err,response){doCallback(callback,[err,response,self],self)})},Usergrid.Group.prototype.createGroupActivity=function(options,callback){var self=this,user=options.user,entity=new Usergrid.Entity({client:this._client,data:{actor:{displayName:user.get("username"),uuid:user.get("uuid"),username:user.get("username"),email:user.get("email"),picture:user.get("picture"),image:{duration:0,height:80,url:user.get("picture"),width:80}},verb:"post",content:options.content,type:"groups/"+this._path+"/activities"}});entity.save(function(err,response){doCallback(callback,[err,response,self])})},Usergrid.Counter=function(options){this._client=options.client,this._data=options.data||{},this._data.category=options.category||"UNKNOWN",this._data.timestamp=options.timestamp||0,this._data.type="events",this._data.counters=options.counters||{}};var COUNTER_RESOLUTIONS=["all","minute","five_minutes","half_hour","hour","six_day","day","week","month"];Usergrid.Counter.prototype=new Usergrid.Entity,Usergrid.Counter.prototype.fetch=function(callback){this.getData({},callback)},Usergrid.Counter.prototype.increment=function(options,callback){var self=this,name=options.name,value=options.value;return name?isNaN(value)?doCallback(callback,[new UsergridInvalidArgumentError("'value' for increment, decrement must be a number"),null,self],self):(self._data.counters[name]=parseInt(value)||1,self.save(callback)):doCallback(callback,[new UsergridInvalidArgumentError("'name' for increment, decrement must be a number"),null,self],self)},Usergrid.Counter.prototype.decrement=function(options,callback){var self=this,name=options.name,value=options.value;self.increment({name:name,value:-(parseInt(value)||1)},callback)},Usergrid.Counter.prototype.reset=function(options,callback){var self=this,name=options.name;self.increment({name:name,value:0},callback)},Usergrid.Counter.prototype.getData=function(options,callback){var start_time,end_time,start=options.start||0,end=options.end||Date.now(),resolution=(options.resolution||"all").toLowerCase(),counters=options.counters||Object.keys(this._data.counters),res=(resolution||"all").toLowerCase();-1===COUNTER_RESOLUTIONS.indexOf(res)&&(res="all"),start_time=getSafeTime(start),end_time=getSafeTime(end);var self=this,params=Object.keys(counters).map(function(counter){return["counter",encodeURIComponent(counters[counter])].join("=")});params.push("resolution="+res),params.push("start_time="+String(start_time)),params.push("end_time="+String(end_time));var endpoint="counters?"+params.join("&");this._client.request({endpoint:endpoint},function(err,data){return data.counters&&data.counters.length&&data.counters.forEach(function(counter){self._data.counters[counter.name]=counter.value||counter.values}),doCallback(callback,[err,data,self],self)})},Usergrid.Folder=function(options,callback){var self=this;console.log("FOLDER OPTIONS",options),self._client=options.client,self._data=options.data||{},self._data.type="folders";var missingData=["name","owner","path"].some(function(required){return!(required in self._data)});return missingData?doCallback(callback,[new UsergridInvalidArgumentError("Invalid asset data: 'name', 'owner', and 'path' are required properties."),null,self],self):void self.save(function(err,response){err?doCallback(callback,[new UsergridError(response),response,self],self):(response&&response.entities&&response.entities.length&&self.set(response.entities[0]),doCallback(callback,[null,response,self],self))})},Usergrid.Folder.prototype=new Usergrid.Entity,Usergrid.Folder.prototype.fetch=function(callback){var self=this;Usergrid.Entity.prototype.fetch.call(self,function(err,data){console.log("self",self.get()),console.log("data",data),err?doCallback(callback,[null,data,self],self):self.getAssets(function(err,response){err?doCallback(callback,[new UsergridError(response),resonse,self],self):doCallback(callback,[null,self],self)})})},Usergrid.Folder.prototype.addAsset=function(options,callback){var self=this;if("asset"in options){var asset=null;switch(typeof options.asset){case"object":asset=options.asset,asset instanceof Usergrid.Entity||(asset=new Usergrid.Asset(asset));break;case"string":isUUID(options.asset)&&(asset=new Usergrid.Asset({client:self._client,data:{uuid:options.asset,type:"assets"}}))}asset&&asset instanceof Usergrid.Entity&&asset.fetch(function(err,data){if(err)doCallback(callback,[new UsergridError(data),data,self],self);else{var endpoint=["folders",self.get("uuid"),"assets",asset.get("uuid")].join("/"),options={method:"POST",endpoint:endpoint};self._client.request(options,callback)}})}else doCallback(callback,[new UsergridInvalidArgumentError("No asset specified"),null,self],self)},Usergrid.Folder.prototype.removeAsset=function(options,callback){var self=this;if("asset"in options){var asset=null;switch(typeof options.asset){case"object":asset=options.asset;break;case"string":isUUID(options.asset)&&(asset=new Usergrid.Asset({client:self._client,data:{uuid:options.asset,type:"assets"}}))}if(asset&&null!==asset){var endpoint=["folders",self.get("uuid"),"assets",asset.get("uuid")].join("/");self._client.request({method:"DELETE",endpoint:endpoint},function(err,response){err?doCallback(callback,[new UsergridError(response),response,self],self):doCallback(callback,[null,response,self],self)})}}else doCallback(callback,[new UsergridInvalidArgumentError("No asset specified"),null,self],self)},Usergrid.Folder.prototype.getAssets=function(callback){return this.getConnections("assets",callback)},XMLHttpRequest.prototype.sendAsBinary||(XMLHttpRequest.prototype.sendAsBinary=function(sData){for(var nBytes=sData.length,ui8Data=new Uint8Array(nBytes),nIdx=0;nBytes>nIdx;nIdx++)ui8Data[nIdx]=255&sData.charCodeAt(nIdx);this.send(ui8Data)}),Usergrid.Asset=function(options,callback){var self=this;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)});missingData?doCallback(callback,[new UsergridError("Invalid asset data: 'name', 'owner', and 'path' are required properties."),null,self],self):self.save(function(err,data){err?doCallback(callback,[new UsergridError(data),data,self],self):(data&&data.entities&&data.entities.length&&self.set(data.entities[0]),doCallback(callback,[null,data,self],self))})},Usergrid.Asset.prototype=new Usergrid.Entity,Usergrid.Asset.prototype.addToFolder=function(options,callback){var self=this;if("folder"in options&&isUUID(options.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("/"),options={method:"POST",endpoint:endpoint};this._client.request(options,function(err,response){err?doCallback(callback,[UsergridError.fromResponse(folder),response,self],self):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))return void doCallback(callback,[new UsergridError("The File APIs are not fully supported by your browser."),null,this],this);var self=this,args=arguments,type=this._data.type,attempts=self.get("attempts");if(isNaN(attempts)&&(attempts=3),"assets"!=type&&"asset"!=type)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,!0),xhr.onerror=function(){doCallback(callback,[new UsergridError("The File APIs are not fully supported by your browser.")],xhr,self)},xhr.onload=function(){xhr.status>=500&&attempts>0?(self.set("attempts",--attempts),setTimeout(function(){self.attachAsset.apply(self,args)},100)):xhr.status>=300?(self.set("attempts"),doCallback(callback,[new UsergridError(JSON.parse(xhr.responseText)),xhr,self],self)):(self.set("attempts"),self.fetch(),doCallback(callback,[null,xhr,self],self))};var fr=new FileReader;fr.onload=function(){var binary=fr.result;("assets"===type||"asset"===type)&&(xhr.overrideMimeType("application/octet-stream"),xhr.setRequestHeader("Content-Type","application/octet-stream")),xhr.sendAsBinary(binary)},fr.readAsBinaryString(file)},Usergrid.Asset.prototype.upload=function(data,callback){this.attachAsset(data,function(err,response){err?doCallback(callback,[new UsergridError(err),response,self],self):doCallback(callback,[null,response,self],self)})},Usergrid.Entity.prototype.downloadAsset=function(callback){var endpoint,self=this,type=this._data.type,xhr=new XMLHttpRequest;endpoint="assets"!=type&&"asset"!=type?[this._client.URI,this._client.orgName,this._client.appName,type,self.get("uuid")].join("/"):[this._client.URI,this._client.orgName,this._client.appName,"assets",self.get("uuid"),"data"].join("/"),xhr.open("GET",endpoint,!0),xhr.responseType="blob",xhr.onload=function(){var blob=xhr.response;"assets"!=type&&"asset"!=type?doCallback(callback,[null,blob,xhr],self):doCallback(callback,[null,xhr,self],self)},xhr.onerror=function(err){callback(!0,err),doCallback(callback,[new UsergridError(err),xhr,self],self)},"assets"!=type&&"asset"!=type?xhr.setRequestHeader("Accept",self._data["file-metadata"]["content-type"]):xhr.overrideMimeType(self.get("content-type")),xhr.send()},Usergrid.Asset.prototype.download=function(callback){this.downloadAsset(function(err,response){err?doCallback(callback,[new UsergridError(err),response,self],self):doCallback(callback,[null,response,self],self)})},function(global){function UsergridError(message,name,timestamp,duration,exception){this.message=message,this.name=name,this.timestamp=timestamp||Date.now(),this.duration=duration||0,this.exception=exception}function UsergridHTTPResponseError(message,name,timestamp,duration,exception){this.message=message,this.name=name,this.timestamp=timestamp||Date.now(),this.duration=duration||0,this.exception=exception}function UsergridInvalidHTTPMethodError(message,name,timestamp,duration,exception){this.message=message,this.name=name||"invalid_http_method",this.timestamp=timestamp||Date.now(),this.duration=duration||0,this.exception=exception}function UsergridInvalidURIError(message,name,timestamp,duration,exception){this.message=message,this.name=name||"invalid_uri",this.timestamp=timestamp||Date.now(),this.duration=duration||0,this.exception=exception}function UsergridInvalidArgumentError(message,name,timestamp,duration,exception){this.message=message,this.name=name||"invalid_argument",this.timestamp=timestamp||Date.now(),this.duration=duration||0,this.exception=exception}function UsergridKeystoreDatabaseUpgradeNeededError(message,name,timestamp,duration,exception){this.message=message,this.name=name,this.timestamp=timestamp||Date.now(),this.duration=duration||0,this.exception=exception}var short,name="UsergridError",_name=global[name],_short=short&&void 0!==short?global[short]:void 0;return UsergridError.prototype=new Error,UsergridError.prototype.constructor=UsergridError,UsergridError.fromResponse=function(response){return response&&"undefined"!=typeof response?new UsergridError(response.error_description,response.error,response.timestamp,response.duration,response.exception):new UsergridError},UsergridError.createSubClass=function(name){return name in global&&global[name]?global[name]:(global[name]=function(){},global[name].name=name,global[name].prototype=new UsergridError,global[name])},UsergridHTTPResponseError.prototype=new UsergridError,UsergridInvalidHTTPMethodError.prototype=new UsergridError,UsergridInvalidURIError.prototype=new UsergridError,UsergridInvalidArgumentError.prototype=new UsergridError,UsergridKeystoreDatabaseUpgradeNeededError.prototype=new UsergridError,global.UsergridHTTPResponseError=UsergridHTTPResponseError,global.UsergridInvalidHTTPMethodError=UsergridInvalidHTTPMethodError,global.UsergridInvalidURIError=UsergridInvalidURIError,global.UsergridInvalidArgumentError=UsergridInvalidArgumentError,global.UsergridKeystoreDatabaseUpgradeNeededError=UsergridKeystoreDatabaseUpgradeNeededError,global[name]=UsergridError,void 0!==short&&(global[short]=UsergridError),global[name].noConflict=function(){return _name&&(global[name]=_name),void 0!==short&&(global[short]=_short),UsergridError},global[name]}(this);
\ No newline at end of file