├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── changelog.md ├── examples ├── all-calls │ ├── all-calls.html │ └── app.js ├── dogs │ ├── app.js │ └── dogs.html ├── facebook │ ├── app.js │ ├── facebook.html │ └── guide.html ├── persistence │ ├── test.html │ └── test.js ├── resources │ ├── css │ │ ├── bootstrap-combined.min.css │ │ └── styles.css │ ├── images │ │ └── apigee.png │ └── js │ │ └── json2.js └── test │ ├── test.html │ └── test.js ├── extensions └── usergrid.validation.js ├── index.html ├── lib ├── Module.js ├── Usergrid.js └── modules │ ├── Asset.js │ ├── Client.js │ ├── Collection.js │ ├── Counter.js │ ├── Entity.js │ ├── Error.js │ ├── Folder.js │ ├── Group.js │ └── util │ ├── Ajax.js │ ├── Event.js │ ├── Logger.js │ └── Promise.js ├── package.json ├── tests ├── mocha │ ├── index.html │ └── test.js ├── qunit │ ├── apigee_test.html │ └── tests.js ├── resources │ ├── css │ │ ├── bootstrap-combined.min.css │ │ ├── mocha.css │ │ └── styles.css │ ├── images │ │ └── apigee.png │ └── js │ │ ├── blanket_mocha.min.js │ │ ├── json2.js │ │ └── mocha.js ├── test.html └── test.js ├── usergrid.js └── usergrid.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | var files = [ 3 | "lib/modules/util/Event.js", 4 | "lib/modules/util/Logger.js", 5 | "lib/modules/util/Promise.js", 6 | "lib/modules/util/Ajax.js", 7 | "lib/Usergrid.js", 8 | "lib/modules/Client.js", 9 | "lib/modules/Entity.js", 10 | "lib/modules/Collection.js", 11 | "lib/modules/Group.js", 12 | "lib/modules/Counter.js", 13 | "lib/modules/Folder.js", 14 | "lib/modules/Asset.js", 15 | "lib/modules/Error.js" 16 | ]; 17 | var tests = ["tests/mocha/index.html", "tests/mocha/test_*.html"]; 18 | // Project configuration. 19 | grunt.initConfig({ 20 | //pkg: grunt.file.readJSON('package.json'), 21 | "meta": { 22 | "package": grunt.file.readJSON("package.json") 23 | }, 24 | "clean": ["usergrid.js", "usergrid.min.js"], 25 | "uglify": { 26 | "unminified": { 27 | "options": { 28 | "banner": "/*! \n\ 29 | *Licensed to the Apache Software Foundation (ASF) under one\n\ 30 | *or more contributor license agreements. See the NOTICE file\n\ 31 | *distributed with this work for additional information\n\ 32 | *regarding copyright ownership. The ASF licenses this file\n\ 33 | *to you under the Apache License, Version 2.0 (the\n\ 34 | *\"License\"); you may not use this file except in compliance\n\ 35 | *with the License. You may obtain a copy of the License at\n\ 36 | *\n\ 37 | * http://www.apache.org/licenses/LICENSE-2.0\n\ 38 | * \n\ 39 | *Unless required by applicable law or agreed to in writing,\n\ 40 | *software distributed under the License is distributed on an\n\ 41 | *\"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n\ 42 | *KIND, either express or implied. See the License for the\n\ 43 | *specific language governing permissions and limitations\n\ 44 | *under the License.\n\ 45 | * \n\ 46 | * \n\ 47 | * <%= meta.package.name %>@<%= meta.package.version %> <%= grunt.template.today('yyyy-mm-dd') %> \n\ 48 | */\n", 49 | "mangle": false, 50 | "compress": false, 51 | "beautify": true, 52 | "preserveComments": function(node, comment){ 53 | //console.log((node.parent_scope!==undefined&&comment.value.indexOf('*Licensed to the Apache Software Foundation')===-1)?"has parent":comment.value); 54 | return comment.type==='comment2'&&comment.value.indexOf('*Licensed to the Apache Software Foundation')===-1; 55 | } 56 | }, 57 | "files": { 58 | "usergrid.js": files 59 | } 60 | }, 61 | "minified": { 62 | "options": { 63 | "banner": "/*! \n\ 64 | *Licensed to the Apache Software Foundation (ASF) under one\n\ 65 | *or more contributor license agreements. See the NOTICE file\n\ 66 | *distributed with this work for additional information\n\ 67 | *regarding copyright ownership. The ASF licenses this file\n\ 68 | *to you under the Apache License, Version 2.0 (the\n\ 69 | *\"License\"); you may not use this file except in compliance\n\ 70 | *with the License. You may obtain a copy of the License at\n\ 71 | *\n\ 72 | * http://www.apache.org/licenses/LICENSE-2.0\n\ 73 | * \n\ 74 | *Unless required by applicable law or agreed to in writing,\n\ 75 | *software distributed under the License is distributed on an\n\ 76 | *\"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n\ 77 | *KIND, either express or implied. See the License for the\n\ 78 | *specific language governing permissions and limitations\n\ 79 | *under the License.\n\ 80 | * \n\ 81 | * \n\ 82 | * <%= meta.package.name %>@<%= meta.package.version %> <%= grunt.template.today('yyyy-mm-dd') %> \n\ 83 | */\n", 84 | "mangle": false, 85 | "compress": true, 86 | "beautify": false, 87 | "preserveComments": "some" 88 | }, 89 | "files": { 90 | "usergrid.min.js": files 91 | } 92 | } 93 | }, 94 | "connect": { 95 | "server": { 96 | "options": { 97 | "port": 3000, 98 | "base": "." 99 | } 100 | }, 101 | "test": { 102 | "options": { 103 | "port": 8000, 104 | "base": "." 105 | } 106 | } 107 | }, 108 | "watch": { 109 | "files": [files, 'Gruntfile.js'], 110 | "tasks": ["default"] 111 | }, 112 | "blanket_mocha": { 113 | //"all": tests, 114 | urls: [ 'http://localhost:8000/tests/mocha/index.html' ], 115 | "options": { 116 | "dest": "report/coverage.html", 117 | "reporter": "Spec", 118 | "threshold": 70 119 | } 120 | } 121 | }); 122 | grunt.loadNpmTasks("grunt-contrib-clean"); 123 | grunt.loadNpmTasks("grunt-contrib-uglify"); 124 | grunt.loadNpmTasks("grunt-contrib-watch"); 125 | grunt.loadNpmTasks("grunt-contrib-connect"); 126 | grunt.loadNpmTasks("grunt-blanket-mocha"); 127 | grunt.registerTask("default", [ 128 | "clean", 129 | "uglify" 130 | ]); 131 | grunt.registerTask("dev", [ 132 | "connect:server", 133 | "watch" 134 | ]); 135 | grunt.registerTask("test", [ 136 | "connect:test", 137 | "blanket_mocha" 138 | ]); 139 | }; 140 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache Usergrid itself is licensed under the terms of the Apache License: 3 | 4 | Apache License 5 | Version 2.0, January 2004 6 | http://www.apache.org/licenses/ 7 | 8 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 9 | 10 | 1. Definitions. 11 | 12 | "License" shall mean the terms and conditions for use, reproduction, 13 | and distribution as defined by Sections 1 through 9 of this document. 14 | 15 | "Licensor" shall mean the copyright owner or entity authorized by 16 | the copyright owner that is granting the License. 17 | 18 | "Legal Entity" shall mean the union of the acting entity and all 19 | other entities that control, are controlled by, or are under common 20 | control with that entity. For the purposes of this definition, 21 | "control" means (i) the power, direct or indirect, to cause the 22 | direction or management of such entity, whether by contract or 23 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 24 | outstanding shares, or (iii) beneficial ownership of such entity. 25 | 26 | "You" (or "Your") shall mean an individual or Legal Entity 27 | exercising permissions granted by this License. 28 | 29 | "Source" form shall mean the preferred form for making modifications, 30 | including but not limited to software source code, documentation 31 | source, and configuration files. 32 | 33 | "Object" form shall mean any form resulting from mechanical 34 | transformation or translation of a Source form, including but 35 | not limited to compiled object code, generated documentation, 36 | and conversions to other media types. 37 | 38 | "Work" shall mean the work of authorship, whether in Source or 39 | Object form, made available under the License, as indicated by a 40 | copyright notice that is included in or attached to the work 41 | (an example is provided in the Appendix below). 42 | 43 | "Derivative Works" shall mean any work, whether in Source or Object 44 | form, that is based on (or derived from) the Work and for which the 45 | editorial revisions, annotations, elaborations, or other modifications 46 | represent, as a whole, an original work of authorship. For the purposes 47 | of this License, Derivative Works shall not include works that remain 48 | separable from, or merely link (or bind by name) to the interfaces of, 49 | the Work and Derivative Works thereof. 50 | 51 | "Contribution" shall mean any work of authorship, including 52 | the original version of the Work and any modifications or additions 53 | to that Work or Derivative Works thereof, that is intentionally 54 | submitted to Licensor for inclusion in the Work by the copyright owner 55 | or by an individual or Legal Entity authorized to submit on behalf of 56 | the copyright owner. For the purposes of this definition, "submitted" 57 | means any form of electronic, verbal, or written communication sent 58 | to the Licensor or its representatives, including but not limited to 59 | communication on electronic mailing lists, source code control systems, 60 | and issue tracking systems that are managed by, or on behalf of, the 61 | Licensor for the purpose of discussing and improving the Work, but 62 | excluding communication that is conspicuously marked or otherwise 63 | designated in writing by the copyright owner as "Not a Contribution." 64 | 65 | "Contributor" shall mean Licensor and any individual or Legal Entity 66 | on behalf of whom a Contribution has been received by Licensor and 67 | subsequently incorporated within the Work. 68 | 69 | 2. Grant of Copyright License. Subject to the terms and conditions of 70 | this License, each Contributor hereby grants to You a perpetual, 71 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 72 | copyright license to reproduce, prepare Derivative Works of, 73 | publicly display, publicly perform, sublicense, and distribute the 74 | Work and such Derivative Works in Source or Object form. 75 | 76 | 3. Grant of Patent License. Subject to the terms and conditions of 77 | this License, each Contributor hereby grants to You a perpetual, 78 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 79 | (except as stated in this section) patent license to make, have made, 80 | use, offer to sell, sell, import, and otherwise transfer the Work, 81 | where such license applies only to those patent claims licensable 82 | by such Contributor that are necessarily infringed by their 83 | Contribution(s) alone or by combination of their Contribution(s) 84 | with the Work to which such Contribution(s) was submitted. If You 85 | institute patent litigation against any entity (including a 86 | cross-claim or counterclaim in a lawsuit) alleging that the Work 87 | or a Contribution incorporated within the Work constitutes direct 88 | or contributory patent infringement, then any patent licenses 89 | granted to You under this License for that Work shall terminate 90 | as of the date such litigation is filed. 91 | 92 | 4. Redistribution. You may reproduce and distribute copies of the 93 | Work or Derivative Works thereof in any medium, with or without 94 | modifications, and in Source or Object form, provided that You 95 | meet the following conditions: 96 | 97 | (a) You must give any other recipients of the Work or 98 | Derivative Works a copy of this License; and 99 | 100 | (b) You must cause any modified files to carry prominent notices 101 | stating that You changed the files; and 102 | 103 | (c) You must retain, in the Source form of any Derivative Works 104 | that You distribute, all copyright, patent, trademark, and 105 | attribution notices from the Source form of the Work, 106 | excluding those notices that do not pertain to any part of 107 | the Derivative Works; and 108 | 109 | (d) If the Work includes a "NOTICE" text file as part of its 110 | distribution, then any Derivative Works that You distribute must 111 | include a readable copy of the attribution notices contained 112 | within such NOTICE file, excluding those notices that do not 113 | pertain to any part of the Derivative Works, in at least one 114 | of the following places: within a NOTICE text file distributed 115 | as part of the Derivative Works; within the Source form or 116 | documentation, if provided along with the Derivative Works; or, 117 | within a display generated by the Derivative Works, if and 118 | wherever such third-party notices normally appear. The contents 119 | of the NOTICE file are for informational purposes only and 120 | do not modify the License. You may add Your own attribution 121 | notices within Derivative Works that You distribute, alongside 122 | or as an addendum to the NOTICE text from the Work, provided 123 | that such additional attribution notices cannot be construed 124 | as modifying the License. 125 | 126 | You may add Your own copyright statement to Your modifications and 127 | may provide additional or different license terms and conditions 128 | for use, reproduction, or distribution of Your modifications, or 129 | for any such Derivative Works as a whole, provided Your use, 130 | reproduction, and distribution of the Work otherwise complies with 131 | the conditions stated in this License. 132 | 133 | 5. Submission of Contributions. Unless You explicitly state otherwise, 134 | any Contribution intentionally submitted for inclusion in the Work 135 | by You to the Licensor shall be under the terms and conditions of 136 | this License, without any additional terms or conditions. 137 | Notwithstanding the above, nothing herein shall supersede or modify 138 | the terms of any separate license agreement you may have executed 139 | with Licensor regarding such Contributions. 140 | 141 | 6. Trademarks. This License does not grant permission to use the trade 142 | names, trademarks, service marks, or product names of the Licensor, 143 | except as required for reasonable and customary use in describing the 144 | origin of the Work and reproducing the content of the NOTICE file. 145 | 146 | 7. Disclaimer of Warranty. Unless required by applicable law or 147 | agreed to in writing, Licensor provides the Work (and each 148 | Contributor provides its Contributions) on an "AS IS" BASIS, 149 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 150 | implied, including, without limitation, any warranties or conditions 151 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 152 | PARTICULAR PURPOSE. You are solely responsible for determining the 153 | appropriateness of using or redistributing the Work and assume any 154 | risks associated with Your exercise of permissions under this License. 155 | 156 | 8. Limitation of Liability. In no event and under no legal theory, 157 | whether in tort (including negligence), contract, or otherwise, 158 | unless required by applicable law (such as deliberate and grossly 159 | negligent acts) or agreed to in writing, shall any Contributor be 160 | liable to You for damages, including any direct, indirect, special, 161 | incidental, or consequential damages of any character arising as a 162 | result of this License or out of the use or inability to use the 163 | Work (including but not limited to damages for loss of goodwill, 164 | work stoppage, computer failure or malfunction, or any and all 165 | other commercial damages or losses), even if such Contributor 166 | has been advised of the possibility of such damages. 167 | 168 | 9. Accepting Warranty or Additional Liability. While redistributing 169 | the Work or Derivative Works thereof, You may choose to offer, 170 | and charge a fee for, acceptance of support, warranty, indemnity, 171 | or other liability obligations and/or rights consistent with this 172 | License. However, in accepting such obligations, You may act only 173 | on Your own behalf and on Your sole responsibility, not on behalf 174 | of any other Contributor, and only if You agree to indemnify, 175 | defend, and hold each Contributor harmless for any liability 176 | incurred by, or claims asserted against, such Contributor by reason 177 | of your accepting any such warranty or additional liability. 178 | 179 | END OF TERMS AND CONDITIONS 180 | 181 | APPENDIX: How to apply the Apache License to your work. 182 | 183 | To apply the Apache License to your work, attach the following 184 | boilerplate notice, with the fields enclosed by brackets "[]" 185 | replaced with your own identifying information. (Don't include 186 | the brackets!) The text should be enclosed in the appropriate 187 | comment syntax for the file format. We also recommend that a 188 | file or class name and description of purpose be included on the 189 | same "printed page" as the copyright notice for easier 190 | identification within third-party archives. 191 | 192 | Copyright [yyyy] [name of copyright owner] 193 | 194 | Licensed under the Apache License, Version 2.0 (the "License"); 195 | you may not use this file except in compliance with the License. 196 | You may obtain a copy of the License at 197 | 198 | http://www.apache.org/licenses/LICENSE-2.0 199 | 200 | Unless required by applicable law or agreed to in writing, software 201 | distributed under the License is distributed on an "AS IS" BASIS, 202 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 203 | See the License for the specific language governing permissions and 204 | limitations under the License. 205 | 206 | ------------------------------------------------------------------------------ 207 | 208 | USERGRID SUBCOMPONENTS 209 | 210 | The Usergrid software includes a number of subcomponents with separate 211 | copyrights and license terms. Your use of the source code for these 212 | subcomponents is subject to the terms and conditions of the following 213 | licenses. 214 | 215 | IOS SDK 216 | ------- 217 | For the SBJson component: 218 | 219 | Copyright (c) Stig Brautaset. All rights reserved. 220 | 221 | Redistribution and use in source and binary forms, with or without 222 | modification, are permitted provided that the following conditions are met: 223 | 224 | * Redistributions of source code must retain the above copyright notice, this 225 | list of conditions and the following disclaimer. 226 | 227 | * Redistributions in binary form must reproduce the above copyright notice, 228 | this list of conditions and the following disclaimer in the documentation 229 | and/or other materials provided with the distribution. 230 | 231 | * Neither the name of the author nor the names of its contributors may be used 232 | to endorse or promote products derived from this software without specific 233 | prior written permission. 234 | 235 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 236 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 237 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 238 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 239 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 240 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 241 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 242 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 243 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 244 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 245 | 246 | For the SSKeychain component: 247 | ----------------------------- 248 | 249 | Copyright (c) Sam Soffes, http://soff.es 250 | 251 | Permission is hereby granted, free of charge, to any person obtaining 252 | a copy of this software and associated documentation files (the 253 | "Software"), to deal in the Software without restriction, including 254 | without limitation the rights to use, copy, modify, merge, publish, 255 | distribute, sublicense, and/or sell copies of the Software, and to 256 | permit persons to whom the Software is furnished to do so, subject to 257 | the following conditions: 258 | 259 | The above copyright notice and this permission notice shall be 260 | included in all copies or substantial portions of the Software. 261 | 262 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 263 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 264 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 265 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 266 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 267 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 268 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 269 | 270 | Other components: 271 | ----------------- 272 | 273 | This product bundles angular.js 274 | Copyright(c) Google, Inc. Released under the MIT license. 275 | 276 | This product bundles angular-scenario.js, part of jQuery JavaScript Library 277 | which Includes Sizzle.js Copyright (c) jQuery Foundation, Inc. and others. 278 | Released under the MIT license. 279 | 280 | This product bundles Bootstrap Copyright (c) Twitter, Inc 281 | Licensed under the MIT license. 282 | 283 | The product bundles Intro.js (MIT licensed) 284 | Copyright (c) usabli.ca - A weekend project by Afshin Mehrabani (@afshinmeh) 285 | 286 | This product bundles jQuery 287 | Licensed under MIT license. 288 | 289 | This product bundles jQuery-UI 290 | Licensed under MIT license. 291 | 292 | This product bundles jQuery Sparklines (New BSD License) 293 | Copyright (c) Splunk Inc. 294 | 295 | This product bundles Mocha. 296 | All rights reserved. Licensed under MIT. 297 | Copyright (c) TJ Holowaychuk 298 | 299 | This product bundles NewtonSoft.Json under MIT license 300 | 301 | This product bundles NPM MD5 (BSD-3 licensed) 302 | Copyright (c) Paul Vorbach and Copyright (C), Jeff Mott. 303 | 304 | This product bundles NSubsttute under BSD license 305 | 306 | This product bundles SBJson, which is available under a "3-clause BSD" license. 307 | For details, see sdks/ios/UGAPI/SBJson/ . 308 | 309 | This product bundles Sphinx under BSD license 310 | 311 | This product bundles SSKeychain, which is available under a "MIT/X11" license. 312 | For details, see sdks/ios/UGAPI/SSKeychain/. 313 | 314 | This product bundles SSToolkit. 315 | Copyright (c) Sam Soffes. All rights reserved. 316 | These files can be located within the /sdks/ios package. 317 | 318 | This product bundles Entypo, CC by SA license 319 | 320 | This product bundles date.min.js, MIT license 321 | 322 | This product bundles jquery.ui.timepicker.min.js, MIT license 323 | 324 | This product bundles blanket_mocha.min.js, MIT license 325 | 326 | This product bundles FontAwesome, SIL Open Font License 327 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | ##Change log 2 | ###0.11.0 3 | - Removed 'getOnExist' flag from createEntity and createGroup. Handling of duplicate entity errors is now the responsibility of the client. 4 | - Usergrid.Group.prototype.fetch returns self instead of group list 5 | - Usergrid.Client.prototype.createGroup updated to new callback format 6 | - Usergrid.Client.prototype.createEntity updated to new callback format 7 | - Usergrid.Client.prototype.getEntity updated to new callback format 8 | - Usergrid.Collection instantiation no longer fetches the collection automatically, this no longer takes a callback 9 | - Usergrid.Entity.prototype.save no longer handles password changes 10 | - Usergrid.Entity.prototype.changePassword added to handle password requests 11 | - Usergrid.Counter no longer needs a callback 12 | - Usergrid.Asset.prototype.download sets appropriate mime type for content 13 | - Usergrid.Asset.prototype.upload implemented retry interval to mitigate errors when an asset has not fully propagated 14 | 15 | 16 | ###0.10.8 17 | - Added support for Events and Counters 18 | - Added support for Folders and Assets 19 | - Improved asynchronous call support 20 | - Improved callback handling 21 | - Numerous bug fixes 22 | 23 | ###0.10.7 24 | - Bug fixes 25 | - most calls now return the raw data as the last parameter (called data) 26 | - added some management functions for authentication 27 | - added some methods to pull related data and append to an entity 28 | - 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) 29 | 30 | ###0.10.4 31 | 32 | - Added new functions for creating, getting, and deleting connections 33 | - Added test cases for said functions 34 | - Added logout call to client create to clear out any remnant token from a past session 35 | - Fixed change password error 36 | - Added getEntity method to get existing entity from server 37 | 38 | ###0.10.3 39 | 40 | - Added set / get token methods to accomodate session storage 41 | - Added createUserActivity method to make creating activities for logged in user easier 42 | 43 | ###0.10.2 44 | 45 | - Removed local caching of user object in client 46 | 47 | ###0.10.1 48 | 49 | - Complete refactor of the SDK to bring congruity with the App services Node module 50 | 51 | - Client object is now main entry point - all objects are created from the client, and all calls are run from the client 52 | 53 | - Removed Curl extension - now just use boolean in options object when creating client 54 | 55 | - Added full test coverage for all sample code in the readme file 56 | 57 | - Renamed SDK file to usergrid.js 58 | 59 | 60 | ###0.9.10 61 | 62 | - Refactored directory structure. SDK file now at root, extensions in their own directory, examples in their own directory. 63 | 64 | - 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. 65 | 66 | - 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. 67 | 68 | - 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. 69 | 70 | - Removed deprecated get function from Collection object. 71 | 72 | - Added timeout callback. 73 | 74 | - Added beginnings of a qUnit test suite - only a few functions tested so far, but we will add to the suite as we progress. 75 | 76 | - Removed minified files. We hope to host these files on a CDN soon. 77 | -------------------------------------------------------------------------------- /examples/all-calls/all-calls.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | All Calls Apigee App Services (Usergrid) Example 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | App Services (Usergrid) Javascript SDK Example 31 |
32 | 35 |
36 | This sample application is a quick example to show how to make App Services (Usergrid) calls - GET, POST, PUT, DELETE - with our Javascript SDK. 37 | Also included is a login example. For more information on App Services, see our docs. 38 |
39 |
40 |

Select method to test:

41 | 48 | 49 |
50 |
Method: GET
51 |
52 |
53 |
54 | 55 | 56 |   57 | 58 |   59 |
60 |
61 |
62 |

63 | To run a GET 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 64 | to: https://api.usergrid.com/your-org/your-app/users/fred, and will retrieve the record for fred. 65 |

66 | Note: If you get an error here, it probably means "fred" doesn't exist. Choose "POST" to create a new "fred". 67 |

68 |
GET API Response
69 | 70 |
71 | 72 |
73 |
Method: POST
74 |
75 |
76 |
77 | 78 | 79 | 80 | 81 | 82 |   83 | 84 |   85 |
86 |
87 |
88 |

89 | To run a POST 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 90 | of users, and the JSON request body of, "{"username":"fred"}" will create a new user called "fred". 91 |

92 | Note: 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 93 |

94 |
POST API Response
95 |
96 | 97 |
98 |
Method: PUT
99 |
100 |
101 |
102 | 103 | 104 | 105 | 106 | 107 |   108 | 109 |   110 |
111 |
112 |
113 |

114 | To run a PUT 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 115 | path of users/fred, and the JSON Request Body of "{"othervalue":"12345"}" will update the "fred" entity with the JSON Request Body. 116 |

117 | Note: If you get an error here, it probably means "fred" doesn't exist. Choose "POST" to create a new "fred". 118 |

119 |
PUT API Response
120 |
121 | 122 |
123 |
Method: DELETE
124 |
125 |
126 |
127 | 128 | 129 |   130 | 131 |   132 |
133 |
134 |
135 |

136 | To run a DELETE 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 137 | path of users/fred, and the JSON Request Body of "{"othervalue":"12345"}" will update the "fred" entity with the JSON Request Body. 138 |

139 | Note: If you get an error here, it probably means "fred" doesn't exist. Choose "POST" to create a new "fred". 140 |

141 |
DELETE API Response
142 |
143 | 144 |
145 |
Method: Log In
146 |
147 |
148 |
149 | 150 | 151 | 152 | 153 | 155 | 156 |   157 | 158 |   159 |
160 |
161 |
162 |

163 | To test a login 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 164 | "barney", as created in the default "POST" method. 165 |

166 | Note: 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". 167 |

168 |
Log In API Response
169 |
170 | 171 |
172 | // Press 'Run Query' to send the API call. 173 |
174 |
175 | 176 | 177 | -------------------------------------------------------------------------------- /examples/all-calls/app.js: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed to the Apache Software Foundation (ASF) under one or more 3 | // contributor license agreements. See the NOTICE file distributed with 4 | // this work for additional information regarding copyright ownership. 5 | // The ASF licenses this file to You under the Apache License, Version 2.0 6 | // (the "License"); you may not use this file except in compliance with 7 | // the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | 18 | /* 19 | * All Calls is a sample app that is powered by Usergrid 20 | * This app shows how to make the 4 REST calls (GET, POST, 21 | * PUT, DELETE) against the usergrid API. 22 | * 23 | * Learn more at http://Usergrid.com/docs 24 | * 25 | * Copyright 2012 Apigee Corporation 26 | * 27 | * Licensed under the Apache License, Version 2.0 (the "License"); 28 | * you may not use this file except in compliance with the License. 29 | * You may obtain a copy of the License at 30 | * 31 | * http://www.apache.org/licenses/LICENSE-2.0 32 | * 33 | * Unless required by applicable law or agreed to in writing, software 34 | * distributed under the License is distributed on an "AS IS" BASIS, 35 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 36 | * See the License for the specific language governing permissions and 37 | * limitations under the License. 38 | */ 39 | 40 | /** 41 | * @file app.js 42 | * @author Rod Simpson (rod@apigee.com) 43 | * 44 | * This file contains the main program logic for All Calls App. 45 | */ 46 | $(document).ready(function () { 47 | //first set the org / app path (must be orgname / appname or org id / app id - can't mix names and uuids!!) 48 | 49 | var client = new Usergrid.Client({ 50 | orgName:'yourorgname', 51 | appName:'yourappname', 52 | logging: true, //optional - turn on logging, off by default 53 | buildCurl: true //optional - turn on curl commands, off by default 54 | }); 55 | 56 | function hideAllSections(){ 57 | $('#get-page').hide(); 58 | $('#get-nav').removeClass('active'); 59 | $('#post-page').hide(); 60 | $('#post-nav').removeClass('active'); 61 | $('#put-page').hide(); 62 | $('#put-nav').removeClass('active'); 63 | $('#delete-page').hide(); 64 | $('#delete-nav').removeClass('active'); 65 | $('#login-page').hide(); 66 | $('#login-nav').removeClass('active'); 67 | $('#response').html("// Press 'Run Query' to send the API call."); 68 | } 69 | //bind the show buttons 70 | $('#show-get').bind('click', function() { 71 | hideAllSections(); 72 | $('#get-nav').addClass('active'); 73 | $('#get-page').show(); 74 | }); 75 | 76 | $('#show-post').bind('click', function() { 77 | hideAllSections(); 78 | $('#post-nav').addClass('active'); 79 | $('#post-page').show(); 80 | }); 81 | 82 | $('#show-put').bind('click', function() { 83 | hideAllSections(); 84 | $('#put-nav').addClass('active'); 85 | $('#put-page').show(); 86 | }); 87 | 88 | $('#show-delete').bind('click', function() { 89 | hideAllSections(); 90 | $('#delete-nav').addClass('active'); 91 | $('#delete-page').show(); 92 | }); 93 | 94 | $('#show-login').bind('click', function() { 95 | hideAllSections(); 96 | $('#login-nav').addClass('active'); 97 | $('#login-page').show(); 98 | }); 99 | 100 | $('#run-get').bind('click', function() { 101 | _get(); 102 | }); 103 | 104 | $('#run-post').bind('click', function() { 105 | _post(); 106 | }); 107 | 108 | $('#run-put').bind('click', function() { 109 | _put(); 110 | }); 111 | 112 | $('#run-delete').bind('click', function() { 113 | _delete(); 114 | }); 115 | 116 | $('#run-login').bind('click', function() { 117 | _login(); 118 | }); 119 | 120 | //start with the get page showing by default 121 | $('#get-page').show(); 122 | 123 | //bind the create new dog button 124 | $('#main-menu').bind('click', function() { 125 | $('#get-page').hide(); 126 | $('#post-page').hide(); 127 | $('#put-page').hide(); 128 | $('#delete-page').hide(); 129 | $('#login-page').hide(); 130 | $('#main').show(); 131 | $("#response").html(''); 132 | }); 133 | 134 | function _get() { 135 | var endpoint = $("#get-path").val(); 136 | 137 | var options = { 138 | method:'GET', 139 | endpoint:endpoint 140 | }; 141 | client.request(options, function (err, data) { 142 | //data will contain raw results from API call 143 | if (err) { 144 | var output = JSON.stringify(data, null, 2); 145 | $("#response").html('
ERROR: '+output+'
'); 146 | } else { 147 | var output = JSON.stringify(data, null, 2); 148 | $("#response").html('
'+output+'
'); 149 | } 150 | }); 151 | } 152 | 153 | function _post() { 154 | var endpoint = $("#post-path").val(); 155 | var data = $("#post-data").val(); 156 | data = JSON.parse(data); 157 | 158 | var options = { 159 | method:'POST', 160 | endpoint:endpoint, 161 | body:data 162 | }; 163 | client.request(options, function (err, data) { 164 | //data will contain raw results from API call 165 | if (err) { 166 | var output = JSON.stringify(data, null, 2); 167 | $("#response").html('
ERROR: '+output+'
'); 168 | } else { 169 | var output = JSON.stringify(data, null, 2); 170 | $("#response").html('
'+output+'
'); 171 | } 172 | }); 173 | } 174 | 175 | function _put() { 176 | var endpoint = $("#put-path").val(); 177 | var data = $("#put-data").val(); 178 | data = JSON.parse(data); 179 | 180 | var options = { 181 | method:'PUT', 182 | endpoint:endpoint, 183 | body:data 184 | }; 185 | client.request(options, function (err, data) { 186 | //data will contain raw results from API call 187 | if (err) { 188 | var output = JSON.stringify(data, null, 2); 189 | $("#response").html('
ERROR: '+output+'
'); 190 | } else { 191 | var output = JSON.stringify(data, null, 2); 192 | $("#response").html('
'+output+'
'); 193 | } 194 | }); 195 | } 196 | 197 | function _delete() { 198 | var endpoint = $("#delete-path").val(); 199 | 200 | var options = { 201 | method:'DELETE', 202 | endpoint:endpoint 203 | }; 204 | client.request(options, function (err, data) { 205 | //data will contain raw results from API call 206 | if (err) { 207 | var output = JSON.stringify(data, null, 2); 208 | $("#response").html('
ERROR: '+output+'
'); 209 | } else { 210 | var output = JSON.stringify(data, null, 2); 211 | $("#response").html('
'+output+'
'); 212 | } 213 | }); 214 | } 215 | 216 | function _login() { 217 | var username = $("#username").val(); 218 | var password = $("#password").val(); 219 | 220 | client.login(username, password, function (err, data) { 221 | //at this point, the user has been logged in succesfully and the OAuth token for the user has been stored 222 | //however, in this example, we don't want to use that token for the rest of the API calls, so we will now 223 | //reset it. In your app, you will most likely not want to do this, as you are effectively logging the user 224 | //out. Our calls work because we are going against the Sandbox app, which has no restrictions on permissions. 225 | client.token = null; //delete the user's token by setting it to null 226 | if (err) { 227 | var output = JSON.stringify(data, null, 2); 228 | $("#response").html('
ERROR: '+output+'
'); 229 | } else { 230 | var output = JSON.stringify(data, null, 2); 231 | $("#response").html('
'+output+'
'); 232 | } 233 | }); 234 | } 235 | 236 | }); -------------------------------------------------------------------------------- /examples/dogs/app.js: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed to the Apache Software Foundation (ASF) under one or more 3 | // contributor license agreements. See the NOTICE file distributed with 4 | // this work for additional information regarding copyright ownership. 5 | // The ASF licenses this file to You under the Apache License, Version 2.0 6 | // (the "License"); you may not use this file except in compliance with 7 | // the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | 18 | /** 19 | * dogs is a sample app that is powered by Usergrid 20 | * This app shows how to use the Usergrid SDK to connect 21 | * to Usergrid, how to add entities, and how to page through 22 | * a result set of entities 23 | * 24 | * Learn more at http://Usergrid.com/docs 25 | * 26 | * Copyright 2012 Apigee Corporation 27 | * 28 | * Licensed under the Apache License, Version 2.0 (the "License"); 29 | * you may not use this file except in compliance with the License. 30 | * You may obtain a copy of the License at 31 | * 32 | * http://www.apache.org/licenses/LICENSE-2.0 33 | * 34 | * Unless required by applicable law or agreed to in writing, software 35 | * distributed under the License is distributed on an "AS IS" BASIS, 36 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 37 | * See the License for the specific language governing permissions and 38 | * limitations under the License. 39 | */ 40 | 41 | /** 42 | * @file app.js 43 | * @author Rod Simpson (rod@apigee.com) 44 | * 45 | * This file contains the main program logic for Dogs. 46 | */ 47 | $(document).ready(function () { 48 | //first set the org / app path (must be orgname / appname or org id / app id - can't mix names and uuids!!) 49 | var client = new Usergrid.Client({ 50 | orgName:'yourorgname', 51 | appName:'dogs', 52 | logging: true, //optional - turn on logging, off by default 53 | buildCurl: true //optional - turn on curl commands, off by default 54 | }); 55 | 56 | //make a new "dogs" Collection 57 | var options = { 58 | type:'dogs', 59 | qs:{ql:'order by created DESC'} 60 | } 61 | 62 | var dogs; 63 | 64 | client.createCollection(options, function (err, response, collection) { 65 | if (err) { 66 | $('#mydoglist').html('could not load dogs'); 67 | } else { 68 | dogs=collection; 69 | //bind the next button to the proper method in the collection object 70 | $('#next-button').bind('click', function() { 71 | $('#message').html(''); 72 | dogs.getNextPage(function(err, data){ 73 | if (err) { 74 | alert('could not get next page of dogs'); 75 | } else { 76 | drawDogs(); 77 | } 78 | }); 79 | }); 80 | 81 | //bind the previous button to the proper method in the collection object 82 | $('#previous-button').bind('click', function() { 83 | $('#message').html(''); 84 | dogs.getPreviousPage(function(err, data){ 85 | if (err) { 86 | alert('could not get previous page of dogs'); 87 | } else { 88 | drawDogs(); 89 | } 90 | }); 91 | }); 92 | 93 | //bind the new button to show the "create new dog" form 94 | $('#new-dog-button').bind('click', function() { 95 | $('#dogs-list').hide(); 96 | $('#new-dog').show(); 97 | }); 98 | 99 | //bind the create new dog button 100 | $('#create-dog').bind('click', function() { 101 | newdog(); 102 | }); 103 | 104 | //bind the create new dog button 105 | $('#cancel-create-dog').bind('click', function() { 106 | $('#new-dog').hide(); 107 | $('#dogs-list').show(); 108 | drawDogs(); 109 | }); 110 | 111 | function drawDogs() { 112 | dogs.fetch(function(err, data) { 113 | if(err) { 114 | alert('there was an error getting the dogs'); 115 | } else { 116 | //first empty out all the current dogs in the list 117 | $('#mydoglist').empty(); 118 | //then hide the next / previous buttons 119 | $('#next-button').hide(); 120 | $('#previous-button').hide(); 121 | //iterate through all the items in this "page" of data 122 | //make sure we reset the pointer so we start at the beginning 123 | dogs.resetEntityPointer(); 124 | while(dogs.hasNextEntity()) { 125 | //get a reference to the dog 126 | var dog = dogs.getNextEntity(); 127 | //display the dog in the list 128 | $('#mydoglist').append('
  • '+ dog.get('name') + '
  • '); 129 | } 130 | //if there is more data, display a "next" button 131 | if (dogs.hasNextPage()) { 132 | //show the button 133 | $('#next-button').show(); 134 | } 135 | //if there are previous pages, show a "previous" button 136 | if (dogs.hasPreviousPage()) { 137 | //show the button 138 | $('#previous-button').show(); 139 | } 140 | } 141 | }); 142 | } 143 | 144 | function newdog() { 145 | $('#create-dog').addClass("disabled"); 146 | //get the values from the form 147 | var name = $("#name").val(); 148 | 149 | //make turn off all hints and errors 150 | $("#name-help").hide(); 151 | $("#name-control").removeClass('error'); 152 | 153 | //make sure the input was valid 154 | if (Usergrid.validation.validateName(name, function (){ 155 | $("#name").focus(); 156 | $("#name-help").show(); 157 | $("#name-control").addClass('error'); 158 | $("#name-help").html(Usergrid.validation.getNameAllowedChars()); 159 | $('#create-dog').removeClass("disabled");}) 160 | ) { 161 | 162 | //all is well, so make the new dog 163 | //create a new dog and add it to the collection 164 | var options = { 165 | name:name 166 | } 167 | //just pass the options to the addEntity method 168 | //to the collection and it is saved automatically 169 | dogs.addEntity(options, function(err, dog, data) { 170 | if (err) { 171 | //let the user know there was a problem 172 | alert('Oops! There was an error creating the dog.'); 173 | //enable the button so the form will be ready for next time 174 | $('#create-dog').removeClass("disabled"); 175 | } else { 176 | $('#message').html('New dog created!'); 177 | //the save worked, so hide the new dog form 178 | $('#new-dog').hide(); 179 | //then show the dogs list 180 | $('#dogs-list').show(); 181 | //then call the function to get the list again 182 | drawDogs(); 183 | //finally enable the button so the form will be ready for next time 184 | $('#create-dog').removeClass("disabled"); 185 | } 186 | }); 187 | } 188 | } 189 | drawDogs(); 190 | 191 | } 192 | }); 193 | 194 | }); 195 | -------------------------------------------------------------------------------- /examples/dogs/dogs.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | Dogs Example App for Apigee App Services (Usergrid) 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
    32 | App Services (Usergrid) Javascript SDK 33 |
    34 | 37 |
    38 |

    Dogs

    39 |
    40 | Dogs is a simple application that shows how to create entities and collecitons, how to iterate through results, and how to 41 | display larger result sets in a paged format. 42 |
    43 |
    44 |
    45 | 46 |
    47 |
    48 |
    • No dogs yet!
    49 |
    50 | 51 | 52 |
     
    53 |
    54 | 55 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /examples/facebook/app.js: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed to the Apache Software Foundation (ASF) under one or more 3 | // contributor license agreements. See the NOTICE file distributed with 4 | // this work for additional information regarding copyright ownership. 5 | // The ASF licenses this file to You under the Apache License, Version 2.0 6 | // (the "License"); you may not use this file except in compliance with 7 | // the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | 18 | /** 19 | * dogs is a sample app that is powered by Usergrid 20 | * This app shows how to use the Usergrid SDK to connect 21 | * to Usergrid, how to add entities, and how to page through 22 | * a result set of entities 23 | * 24 | * Learn more at http://Usergrid.com/docs 25 | * 26 | * Copyright 2012 Apigee Corporation 27 | * 28 | * Licensed under the Apache License, Version 2.0 (the "License"); 29 | * you may not use this file except in compliance with the License. 30 | * You may obtain a copy of the License at 31 | * 32 | * http://www.apache.org/licenses/LICENSE-2.0 33 | * 34 | * Unless required by applicable law or agreed to in writing, software 35 | * distributed under the License is distributed on an "AS IS" BASIS, 36 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 37 | * See the License for the specific language governing permissions and 38 | * limitations under the License. 39 | */ 40 | 41 | /** 42 | * @file app.js 43 | * @author Rod Simpson (rod@apigee.com) 44 | * 45 | * This file contains the main program logic for Facebook Login Example. 46 | */ 47 | $(document).ready(function () { 48 | //first set the org / app path (must be orgname / appname or org id / app id - can't mix names and uuids!!) 49 | 50 | var client = new Usergrid.Client({ 51 | orgName:'yourorgname', 52 | appName:'sandbox', 53 | logging: true, //optional - turn on logging, off by default 54 | buildCurl: true //optional - turn on curl commands, off by default 55 | }); 56 | 57 | //bind the login button so we can send the user to facebook 58 | $('#login-button').bind('click', function() { 59 | //get the 60 | var apiKey = $("#api-key").val(); 61 | var location = window.location.protocol + '//' + window.location.host; 62 | var path = window.location.pathname; 63 | 64 | var link = "https://www.facebook.com/dialog/oauth?client_id="; 65 | link += apiKey; 66 | link += "&redirect_uri="; 67 | link += location+path 68 | link += "&scope&COMMA_SEPARATED_LIST_OF_PERMISSION_NAMES&response_type=token"; 69 | 70 | //now forward the user to facebook 71 | window.location = link; 72 | }); 73 | //bind the previous button to the proper method in the collection object 74 | $('#logout-button').bind('click', function() { 75 | logout(); 76 | }); 77 | 78 | //load up the facebook api sdk 79 | window.fbAsyncInit = function() { 80 | FB.init({ 81 | appId : '308790195893570', // App ID 82 | channelUrl : '//usergridsdk.dev//examples/channel.html', // Channel File 83 | status : true, // check login status 84 | cookie : true, // enable cookies to allow the server to access the session 85 | xfbml : true // parse XFBML 86 | }); 87 | }; 88 | 89 | function logout() { 90 | FB.logout(function(response) { 91 | client.logoutAppUser(); 92 | var html = "User logged out. \r\n\r\n // Press 'Log in' to log in with Facebook."; 93 | $('#facebook-status').html(html); 94 | }); 95 | } 96 | 97 | function login(facebookAccessToken) { 98 | client.loginFacebook(facebookAccessToken, function(err, response){ 99 | var output = JSON.stringify(response, null, 2); 100 | if (err) { 101 | var html = '
    Oops!  There was an error logging you in. \r\n\r\n';
    102 |         html += 'Error: \r\n' + output+'
    '; 103 | } else { 104 | var html = '
    Hurray!  You have been logged in. \r\n\r\n';
    105 |         html += 'Facebook Token: ' + '\r\n' + facebookAccessToken + '\r\n\r\n';
    106 |         html += 'Facebook Profile data stored in Usergrid: \r\n' + output+'
    '; 107 | } 108 | $('#facebook-status').html(html); 109 | }) 110 | } 111 | 112 | //pull the access token out of the query string 113 | var ql = []; 114 | if (window.location.hash) { 115 | // split up the query string and store in an associative array 116 | var params = window.location.hash.slice(1).split("#"); 117 | var tmp = params[0].split("&"); 118 | for (var i = 0; i < tmp.length; i++) { 119 | var vals = tmp[i].split("="); 120 | ql[vals[0]] = unescape(vals[1]); 121 | } 122 | } 123 | 124 | if (ql['access_token']) { 125 | var facebookAccessToken = ql['access_token'] 126 | //try to log in with facebook 127 | login(facebookAccessToken); 128 | } 129 | }); -------------------------------------------------------------------------------- /examples/facebook/facebook.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | Facebook Login Example App for Apigee App Services (Usergrid) 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
    32 | App Services (Usergrid) Javascript SDK 33 |
    34 | 37 |
    38 | This sample application will show you how to log into App Services (Usergrid) using Facebook and the Usergrid Javascript SDK. 39 | Enter the API Key that you get from Facebook, then log in. 40 |

    41 | The Log in button sends the user to the facebook login page. Once the user logs in, they are redirected back to 42 | this page and automatically logged into Usergrid. If the user is already logged into facebook, then they don't need to log in again. 43 |

    44 | 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. 45 |

    46 | For a step by step walk-thru on how to get this app running, see this guide. 47 |

    48 | For more information on App Services, see our docs site, specifically or our Facebook docs page. 49 |

    50 | For more information on how using the Facebook JS SDK to log users in, see this guide: http://developers.facebook.com/docs/howtos/login/getting-started/ 51 |
    52 |
    53 |
    Log in with Facebook
    54 |
    55 |
    56 |
    57 | 58 | 59 |   60 | 61 | 62 |   63 |
    64 |
    65 |
    66 |
    API Response
    67 |
    68 | // Press 'Log in' to log in with Facebook. 69 |
    70 |
    71 |
    72 | 73 | 74 | -------------------------------------------------------------------------------- /examples/facebook/guide.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
    28 | App Services (Usergrid) Javascript SDK 29 |
    30 |
    31 | To get the Facebook example working on your development machine, follow these steps: 32 |

    33 | 1. Sign up 34 |
    Sign up for a Facebook account. 35 |

    36 | 2. Create a new app 37 |
    Go to https://developers.facebook.com and follow 38 | the prompts to authenticate yourself. Then, go to the Facebook App Dashboard, and create a new app. 39 |

    40 | 3. Specify your Facebook integration method 41 |
    On the app creation page, make sure you specify that you will use "Website with Facebook Login". 42 | You should see something like this. You should enter the url where 43 | you will host this sample (e.g. http://mywebsite.com/path-to-my-code). If want just to test with our gh-pages repo, 44 | located here: http://apigee.github.com/usergrid-javascript-sdk/, 45 | then enter http://apigee.github.com/usergrid-javascript-sdk in the field instead. 46 |

    47 | 4. Get your App Id 48 |
    Once your app is created, you can get the app id by going here: https://developers.facebook.com/apps 49 |

    50 | 5. Download this code (skip this step if you plan to run it on our gh-pages repo) 51 |
    If you haven't done so already, download the Usergrid Javascript SDK: https://github.com/apigee/usergrid-javascript-sdk 52 | and save to a local directory on your hard drive. You will then need to deploy the code to your server. 53 |

    54 | 6. Run the file 55 |
    Navigate your browser to the location where you uploaded your code, or go to our gh-pages repo 56 | ( http://apigee.github.com/usergrid-javascript-sdk/) and choose "Facebook Login Example". 57 |

    58 | 7. Log in! 59 |
    Enter your API key, and follow the prompts. 60 |
    61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /examples/persistence/test.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | Readme File Tests 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 |
    34 | App Services (Usergrid) Javascript SDK 35 |
    36 |
    37 | This sample application runs tests to demonstrate how to persist collections using the collection.serialize() method. 38 |
    39 |
    40 |
    README sample code tests
    41 |
    42 |
    43 |
    44 | 45 |   46 |
    47 |
    48 |
    49 |
    Test Output
    50 |
    51 |
    // Press Start button to begin
    52 |
    53 | 54 | 55 | -------------------------------------------------------------------------------- /examples/persistence/test.js: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed to the Apache Software Foundation (ASF) under one or more 3 | // contributor license agreements. See the NOTICE file distributed with 4 | // this work for additional information regarding copyright ownership. 5 | // The ASF licenses this file to You under the Apache License, Version 2.0 6 | // (the "License"); you may not use this file except in compliance with 7 | // the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | 18 | $(document).ready(function () { 19 | 20 | //call the runner function to start the process 21 | $('#start-button').bind('click', function() { 22 | $('#start-button').attr("disabled", "disabled"); 23 | $('#test-output').html(''); 24 | runner(0); 25 | }); 26 | 27 | var logSuccess = true; 28 | var successCount = 0; 29 | var logError = true; 30 | var errorCount = 0; 31 | var logNotice = true; 32 | 33 | var client = new Usergrid.Client({ 34 | orgName:'yourorgname', 35 | appName:'sandbox', 36 | logging: true, //optional - turn on logging, off by default 37 | buildCurl: true //optional - turn on curl commands, off by default 38 | }); 39 | 40 | function runner(step, arg, arg2){ 41 | step++; 42 | switch(step) 43 | { 44 | case 1: 45 | notice('-----running step '+step+': create and serialize collection'); 46 | createAndSerialzeCollection(step); 47 | break; 48 | case 2: 49 | notice('-----running step '+step+': de-serialize collection'); 50 | deserializeCollection(step); 51 | break; 52 | default: 53 | notice('-----test complete!-----'); 54 | notice('Success count= ' + successCount); 55 | notice('Error count= ' + errorCount); 56 | notice('-----thank you for playing!-----'); 57 | $('#start-button').removeAttr("disabled"); 58 | } 59 | } 60 | 61 | //logging functions 62 | function success(message){ 63 | successCount++; 64 | if (logSuccess) { 65 | console.log('SUCCESS: ' + message); 66 | var html = $('#test-output').html(); 67 | html += ('SUCCESS: ' + message + '\r\n'); 68 | $('#test-output').html(html); 69 | } 70 | } 71 | 72 | function error(message){ 73 | errorCount++ 74 | if (logError) { 75 | console.log('ERROR: ' + message); 76 | var html = $('#test-output').html(); 77 | html += ('ERROR: ' + message + '\r\n'); 78 | $('#test-output').html(html); 79 | } 80 | } 81 | 82 | function notice(message){ 83 | if (logNotice) { 84 | console.log('NOTICE: ' + message); 85 | var html = $('#test-output').html(); 86 | html += (message + '\r\n'); 87 | $('#test-output').html(html); 88 | } 89 | } 90 | 91 | 92 | function createAndSerialzeCollection(step){ 93 | var options = { 94 | type:'books', 95 | qs:{ql:'order by name'} 96 | } 97 | 98 | client.createCollection(options, function (err, books) { 99 | if (err) { 100 | error('could not make collection'); 101 | } else { 102 | 103 | //collection made, now serialize and store 104 | localStorage.setItem('item', books.serialize()); 105 | 106 | success('new Collection created and data stored'); 107 | 108 | runner(step); 109 | 110 | } 111 | }); 112 | } 113 | 114 | 115 | function deserializeCollection(step){ 116 | var books = client.restoreCollection(localStorage.getItem('item')); 117 | 118 | while(books.hasNextEntity()) { 119 | //get a reference to the book 120 | book = books.getNextEntity(); 121 | var name = book.get('name'); 122 | notice('book is called ' + name); 123 | } 124 | 125 | success('looped through books'); 126 | 127 | runner(step); 128 | } 129 | 130 | }); -------------------------------------------------------------------------------- /examples/resources/css/styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * All Calls is a Node.js sample app that is powered by Usergrid 3 | * This app shows how to make the 4 REST calls (GET, POST, 4 | * PUT, DELETE) against the usergrid API. 5 | * 6 | * Learn more at http://Usergrid.com/docs 7 | * 8 | * Copyright 2012 Apigee Corporation 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | */ 22 | 23 | /** 24 | * @file styles.css 25 | * @author Rod Simpson (rod@apigee.com) 26 | * 27 | */ 28 | 29 | body { 30 | background-color: #fff; 31 | min-height: 800px; 32 | } 33 | 34 | /* buttons ================================================================= */ 35 | .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;} 36 | 37 | .header{ 38 | padding: 10px; 39 | width: 100%; 40 | height: 40px; 41 | background-color: #ff4200; 42 | color: #fff; 43 | text-align: left; 44 | font-size: 16px; 45 | font-weight: 800; 46 | } 47 | .breadcrumb{ 48 | font-size: 16px; 49 | } 50 | .info{ 51 | padding: 0px 30px 30px 30px; 52 | font-size: 16px; 53 | } 54 | h3{ 55 | padding-bottom: 20px; 56 | } 57 | .main{ 58 | display: block; 59 | padding: 0 30px 30px 30px ; 60 | background-color: #fff; 61 | } 62 | .form-block{ 63 | display: block; 64 | display: none; 65 | padding: 10px 0; 66 | min-height: 210px; 67 | background-color: #fff; 68 | } 69 | .section-header{ 70 | font-size: 20px; 71 | font-weight: 200; 72 | padding-bottom: 20px; 73 | } 74 | .note { 75 | padding-bottom: 20px; 76 | } 77 | .response-box{ 78 | margin: 0 auto; 79 | padding: 10px; 80 | width: 640px; 81 | border: 1px solid silver; 82 | background-color: #ddd; 83 | font-weight: bold; 84 | } 85 | pre{ 86 | border: none; 87 | padding: 0; 88 | } 89 | .left{ 90 | float: left; 91 | } -------------------------------------------------------------------------------- /examples/resources/images/apigee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apache/usergrid-javascript/3303e561f09d013ba11cece4b024a068f2c8a8d2/examples/resources/images/apigee.png -------------------------------------------------------------------------------- /examples/resources/js/json2.js: -------------------------------------------------------------------------------- 1 | /* 2 | json2.js 3 | 2012-10-08 4 | 5 | Public Domain. 6 | 7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 | 9 | See http://www.JSON.org/js.html 10 | 11 | 12 | This code should be minified before deployment. 13 | See http://javascript.crockford.com/jsmin.html 14 | 15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 16 | NOT CONTROL. 17 | 18 | 19 | This file creates a global JSON object containing two methods: stringify 20 | and parse. 21 | 22 | JSON.stringify(value, replacer, space) 23 | value any JavaScript value, usually an object or array. 24 | 25 | replacer an optional parameter that determines how object 26 | values are stringified for objects. It can be a 27 | function or an array of strings. 28 | 29 | space an optional parameter that specifies the indentation 30 | of nested structures. If it is omitted, the text will 31 | be packed without extra whitespace. If it is a number, 32 | it will specify the number of spaces to indent at each 33 | level. If it is a string (such as '\t' or ' '), 34 | it contains the characters used to indent at each level. 35 | 36 | This method produces a JSON text from a JavaScript value. 37 | 38 | When an object value is found, if the object contains a toJSON 39 | method, its toJSON method will be called and the result will be 40 | stringified. A toJSON method does not serialize: it returns the 41 | value represented by the name/value pair that should be serialized, 42 | or undefined if nothing should be serialized. The toJSON method 43 | will be passed the key associated with the value, and this will be 44 | bound to the value 45 | 46 | For example, this would serialize Dates as ISO strings. 47 | 48 | Date.prototype.toJSON = function (key) { 49 | function f(n) { 50 | // Format integers to have at least two digits. 51 | return n < 10 ? '0' + n : n; 52 | } 53 | 54 | return this.getUTCFullYear() + '-' + 55 | f(this.getUTCMonth() + 1) + '-' + 56 | f(this.getUTCDate()) + 'T' + 57 | f(this.getUTCHours()) + ':' + 58 | f(this.getUTCMinutes()) + ':' + 59 | f(this.getUTCSeconds()) + 'Z'; 60 | }; 61 | 62 | You can provide an optional replacer method. It will be passed the 63 | key and value of each member, with this bound to the containing 64 | object. The value that is returned from your method will be 65 | serialized. If your method returns undefined, then the member will 66 | be excluded from the serialization. 67 | 68 | If the replacer parameter is an array of strings, then it will be 69 | used to select the members to be serialized. It filters the results 70 | such that only members with keys listed in the replacer array are 71 | stringified. 72 | 73 | Values that do not have JSON representations, such as undefined or 74 | functions, will not be serialized. Such values in objects will be 75 | dropped; in arrays they will be replaced with null. You can use 76 | a replacer function to replace those with JSON values. 77 | JSON.stringify(undefined) returns undefined. 78 | 79 | The optional space parameter produces a stringification of the 80 | value that is filled with line breaks and indentation to make it 81 | easier to read. 82 | 83 | If the space parameter is a non-empty string, then that string will 84 | be used for indentation. If the space parameter is a number, then 85 | the indentation will be that many spaces. 86 | 87 | Example: 88 | 89 | text = JSON.stringify(['e', {pluribus: 'unum'}]); 90 | // text is '["e",{"pluribus":"unum"}]' 91 | 92 | 93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 95 | 96 | text = JSON.stringify([new Date()], function (key, value) { 97 | return this[key] instanceof Date ? 98 | 'Date(' + this[key] + ')' : value; 99 | }); 100 | // text is '["Date(---current time---)"]' 101 | 102 | 103 | JSON.parse(text, reviver) 104 | This method parses a JSON text to produce an object or array. 105 | It can throw a SyntaxError exception. 106 | 107 | The optional reviver parameter is a function that can filter and 108 | transform the results. It receives each of the keys and values, 109 | and its return value is used instead of the original value. 110 | If it returns what it received, then the structure is not modified. 111 | If it returns undefined then the member is deleted. 112 | 113 | Example: 114 | 115 | // Parse the text. Values that look like ISO date strings will 116 | // be converted to Date objects. 117 | 118 | myData = JSON.parse(text, function (key, value) { 119 | var a; 120 | if (typeof value === 'string') { 121 | a = 122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 123 | if (a) { 124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 125 | +a[5], +a[6])); 126 | } 127 | } 128 | return value; 129 | }); 130 | 131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 132 | var d; 133 | if (typeof value === 'string' && 134 | value.slice(0, 5) === 'Date(' && 135 | value.slice(-1) === ')') { 136 | d = new Date(value.slice(5, -1)); 137 | if (d) { 138 | return d; 139 | } 140 | } 141 | return value; 142 | }); 143 | 144 | 145 | This is a reference implementation. You are free to copy, modify, or 146 | redistribute. 147 | */ 148 | 149 | /*jslint evil: true, regexp: true */ 150 | 151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 154 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 155 | test, toJSON, toString, valueOf 156 | */ 157 | 158 | 159 | // Create a JSON object only if one does not already exist. We create the 160 | // methods in a closure to avoid creating global variables. 161 | 162 | if (typeof JSON !== 'object') { 163 | JSON = {}; 164 | } 165 | 166 | (function () { 167 | 'use strict'; 168 | 169 | function f(n) { 170 | // Format integers to have at least two digits. 171 | return n < 10 ? '0' + n : n; 172 | } 173 | 174 | if (typeof Date.prototype.toJSON !== 'function') { 175 | 176 | Date.prototype.toJSON = function (key) { 177 | 178 | return isFinite(this.valueOf()) 179 | ? this.getUTCFullYear() + '-' + 180 | f(this.getUTCMonth() + 1) + '-' + 181 | f(this.getUTCDate()) + 'T' + 182 | f(this.getUTCHours()) + ':' + 183 | f(this.getUTCMinutes()) + ':' + 184 | f(this.getUTCSeconds()) + 'Z' 185 | : null; 186 | }; 187 | 188 | String.prototype.toJSON = 189 | Number.prototype.toJSON = 190 | Boolean.prototype.toJSON = function (key) { 191 | return this.valueOf(); 192 | }; 193 | } 194 | 195 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 196 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 197 | gap, 198 | indent, 199 | meta = { // table of character substitutions 200 | '\b': '\\b', 201 | '\t': '\\t', 202 | '\n': '\\n', 203 | '\f': '\\f', 204 | '\r': '\\r', 205 | '"' : '\\"', 206 | '\\': '\\\\' 207 | }, 208 | rep; 209 | 210 | 211 | function quote(string) { 212 | 213 | // If the string contains no control characters, no quote characters, and no 214 | // backslash characters, then we can safely slap some quotes around it. 215 | // Otherwise we must also replace the offending characters with safe escape 216 | // sequences. 217 | 218 | escapable.lastIndex = 0; 219 | return escapable.test(string) ? '"' + string.replace(escapable, function (a) { 220 | var c = meta[a]; 221 | return typeof c === 'string' 222 | ? c 223 | : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 224 | }) + '"' : '"' + string + '"'; 225 | } 226 | 227 | 228 | function str(key, holder) { 229 | 230 | // Produce a string from holder[key]. 231 | 232 | var i, // The loop counter. 233 | k, // The member key. 234 | v, // The member value. 235 | length, 236 | mind = gap, 237 | partial, 238 | value = holder[key]; 239 | 240 | // If the value has a toJSON method, call it to obtain a replacement value. 241 | 242 | if (value && typeof value === 'object' && 243 | typeof value.toJSON === 'function') { 244 | value = value.toJSON(key); 245 | } 246 | 247 | // If we were called with a replacer function, then call the replacer to 248 | // obtain a replacement value. 249 | 250 | if (typeof rep === 'function') { 251 | value = rep.call(holder, key, value); 252 | } 253 | 254 | // What happens next depends on the value's type. 255 | 256 | switch (typeof value) { 257 | case 'string': 258 | return quote(value); 259 | 260 | case 'number': 261 | 262 | // JSON numbers must be finite. Encode non-finite numbers as null. 263 | 264 | return isFinite(value) ? String(value) : 'null'; 265 | 266 | case 'boolean': 267 | case 'null': 268 | 269 | // If the value is a boolean or null, convert it to a string. Note: 270 | // typeof null does not produce 'null'. The case is included here in 271 | // the remote chance that this gets fixed someday. 272 | 273 | return String(value); 274 | 275 | // If the type is 'object', we might be dealing with an object or an array or 276 | // null. 277 | 278 | case 'object': 279 | 280 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 281 | // so watch out for that case. 282 | 283 | if (!value) { 284 | return 'null'; 285 | } 286 | 287 | // Make an array to hold the partial results of stringifying this object value. 288 | 289 | gap += indent; 290 | partial = []; 291 | 292 | // Is the value an array? 293 | 294 | if (Object.prototype.toString.apply(value) === '[object Array]') { 295 | 296 | // The value is an array. Stringify every element. Use null as a placeholder 297 | // for non-JSON values. 298 | 299 | length = value.length; 300 | for (i = 0; i < length; i += 1) { 301 | partial[i] = str(i, value) || 'null'; 302 | } 303 | 304 | // Join all of the elements together, separated with commas, and wrap them in 305 | // brackets. 306 | 307 | v = partial.length === 0 308 | ? '[]' 309 | : gap 310 | ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' 311 | : '[' + partial.join(',') + ']'; 312 | gap = mind; 313 | return v; 314 | } 315 | 316 | // If the replacer is an array, use it to select the members to be stringified. 317 | 318 | if (rep && typeof rep === 'object') { 319 | length = rep.length; 320 | for (i = 0; i < length; i += 1) { 321 | if (typeof rep[i] === 'string') { 322 | k = rep[i]; 323 | v = str(k, value); 324 | if (v) { 325 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 326 | } 327 | } 328 | } 329 | } else { 330 | 331 | // Otherwise, iterate through all of the keys in the object. 332 | 333 | for (k in value) { 334 | if (Object.prototype.hasOwnProperty.call(value, k)) { 335 | v = str(k, value); 336 | if (v) { 337 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 338 | } 339 | } 340 | } 341 | } 342 | 343 | // Join all of the member texts together, separated with commas, 344 | // and wrap them in braces. 345 | 346 | v = partial.length === 0 347 | ? '{}' 348 | : gap 349 | ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' 350 | : '{' + partial.join(',') + '}'; 351 | gap = mind; 352 | return v; 353 | } 354 | } 355 | 356 | // If the JSON object does not yet have a stringify method, give it one. 357 | 358 | if (typeof JSON.stringify !== 'function') { 359 | JSON.stringify = function (value, replacer, space) { 360 | 361 | // The stringify method takes a value and an optional replacer, and an optional 362 | // space parameter, and returns a JSON text. The replacer can be a function 363 | // that can replace values, or an array of strings that will select the keys. 364 | // A default replacer method can be provided. Use of the space parameter can 365 | // produce text that is more easily readable. 366 | 367 | var i; 368 | gap = ''; 369 | indent = ''; 370 | 371 | // If the space parameter is a number, make an indent string containing that 372 | // many spaces. 373 | 374 | if (typeof space === 'number') { 375 | for (i = 0; i < space; i += 1) { 376 | indent += ' '; 377 | } 378 | 379 | // If the space parameter is a string, it will be used as the indent string. 380 | 381 | } else if (typeof space === 'string') { 382 | indent = space; 383 | } 384 | 385 | // If there is a replacer, it must be a function or an array. 386 | // Otherwise, throw an error. 387 | 388 | rep = replacer; 389 | if (replacer && typeof replacer !== 'function' && 390 | (typeof replacer !== 'object' || 391 | typeof replacer.length !== 'number')) { 392 | throw new Error('JSON.stringify'); 393 | } 394 | 395 | // Make a fake root object containing our value under the key of ''. 396 | // Return the result of stringifying the value. 397 | 398 | return str('', {'': value}); 399 | }; 400 | } 401 | 402 | 403 | // If the JSON object does not yet have a parse method, give it one. 404 | 405 | if (typeof JSON.parse !== 'function') { 406 | JSON.parse = function (text, reviver) { 407 | 408 | // The parse method takes a text and an optional reviver function, and returns 409 | // a JavaScript value if the text is a valid JSON text. 410 | 411 | var j; 412 | 413 | function walk(holder, key) { 414 | 415 | // The walk method is used to recursively walk the resulting structure so 416 | // that modifications can be made. 417 | 418 | var k, v, value = holder[key]; 419 | if (value && typeof value === 'object') { 420 | for (k in value) { 421 | if (Object.prototype.hasOwnProperty.call(value, k)) { 422 | v = walk(value, k); 423 | if (v !== undefined) { 424 | value[k] = v; 425 | } else { 426 | delete value[k]; 427 | } 428 | } 429 | } 430 | } 431 | return reviver.call(holder, key, value); 432 | } 433 | 434 | 435 | // Parsing happens in four stages. In the first stage, we replace certain 436 | // Unicode characters with escape sequences. JavaScript handles many characters 437 | // incorrectly, either silently deleting them, or treating them as line endings. 438 | 439 | text = String(text); 440 | cx.lastIndex = 0; 441 | if (cx.test(text)) { 442 | text = text.replace(cx, function (a) { 443 | return '\\u' + 444 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 445 | }); 446 | } 447 | 448 | // In the second stage, we run the text against regular expressions that look 449 | // for non-JSON patterns. We are especially concerned with '()' and 'new' 450 | // because they can cause invocation, and '=' because it can cause mutation. 451 | // But just to be safe, we want to reject all unexpected forms. 452 | 453 | // We split the second stage into 4 regexp operations in order to work around 454 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 455 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 456 | // replace all simple value tokens with ']' characters. Third, we delete all 457 | // open brackets that follow a colon or comma or that begin the text. Finally, 458 | // we look to see that the remaining characters are only whitespace or ']' or 459 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 460 | 461 | if (/^[\],:{}\s]*$/ 462 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') 463 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') 464 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 465 | 466 | // In the third stage we use the eval function to compile the text into a 467 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity 468 | // in JavaScript: it can begin a block or an object literal. We wrap the text 469 | // in parens to eliminate the ambiguity. 470 | 471 | j = eval('(' + text + ')'); 472 | 473 | // In the optional fourth stage, we recursively walk the new structure, passing 474 | // each name/value pair to a reviver function for possible transformation. 475 | 476 | return typeof reviver === 'function' 477 | ? walk({'': j}, '') 478 | : j; 479 | } 480 | 481 | // If the text is not JSON parseable, then a SyntaxError is thrown. 482 | 483 | throw new SyntaxError('JSON.parse'); 484 | }; 485 | } 486 | }()); 487 | -------------------------------------------------------------------------------- /examples/test/test.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | Readme File Tests 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 |
    34 | App Services (Usergrid) Javascript SDK 35 |
    36 |
    37 | 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. 38 |
    39 |
    40 |
    README sample code tests
    41 |
    42 |
    43 |
    44 | 45 |   46 |
    47 |
    48 |
    49 |
    Test Output
    50 |
    51 |
    // Press Start button to begin
    52 |
    53 | 54 | 55 | -------------------------------------------------------------------------------- /extensions/usergrid.validation.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | /** 21 | * validation is a Singleton that provides methods for validating common field types 22 | * 23 | * @class Usergrid.validation 24 | * @author Rod Simpson (rod@apigee.com) 25 | **/ 26 | Usergrid.validation = (function () { 27 | 28 | var usernameRegex = new RegExp("^([0-9a-zA-Z\.\-])+$"); 29 | var nameRegex = new RegExp("^([0-9a-zA-Z@#$%^&!?;:.,'\"~*-=+_\[\\](){}/\\ |])+$"); 30 | var emailRegex = new RegExp("^(([0-9a-zA-Z]+[_\+.-]?)+@[0-9a-zA-Z]+[0-9,a-z,A-Z,.,-]*(.){1}[a-zA-Z]{2,4})+$"); 31 | var passwordRegex = new RegExp("^([0-9a-zA-Z@#$%^&!?<>;:.,'\"~*-=+_\[\\](){}/\\ |])+$"); 32 | var pathRegex = new RegExp("^([0-9a-z./-])+$"); 33 | var titleRegex = new RegExp("^([0-9a-zA-Z.!-?/])+$"); 34 | 35 | /** 36 | * Tests the string against the allowed chars regex 37 | * 38 | * @public 39 | * @method validateUsername 40 | * @param {string} username - The string to test 41 | * @param {function} failureCallback - (optional), the function to call on a failure 42 | * @return {boolean} Returns true if string passes regex, false if not 43 | */ 44 | function validateUsername(username, failureCallback) { 45 | if (usernameRegex.test(username) && checkLength(username, 4, 80)) { 46 | return true; 47 | } else { 48 | if (failureCallback && typeof(failureCallback) === "function") { 49 | failureCallback(this.getUsernameAllowedChars()); 50 | } 51 | return false; 52 | } 53 | } 54 | 55 | /** 56 | * Returns the regex of allowed chars 57 | * 58 | * @public 59 | * @method getUsernameAllowedChars 60 | * @return {string} Returns a string with the allowed chars 61 | */ 62 | function getUsernameAllowedChars(){ 63 | return 'Length: min 4, max 80. Allowed: A-Z, a-z, 0-9, dot, and dash'; 64 | } 65 | 66 | /** 67 | * Tests the string against the allowed chars regex 68 | * 69 | * @public 70 | * @method validateName 71 | * @param {string} name - The string to test 72 | * @param {function} failureCallback - (optional), the function to call on a failure 73 | * @return {boolean} Returns true if string passes regex, false if not 74 | */ 75 | function validateName(name, failureCallback) { 76 | if (nameRegex.test(name) && checkLength(name, 4, 80)) { 77 | return true; 78 | } else { 79 | if (failureCallback && typeof(failureCallback) === "function") { 80 | failureCallback(this.getNameAllowedChars()); 81 | } 82 | return false; 83 | } 84 | } 85 | 86 | /** 87 | * Returns the regex of allowed chars 88 | * 89 | * @public 90 | * @method getNameAllowedChars 91 | * @return {string} Returns a string with the allowed chars 92 | */ 93 | function getNameAllowedChars(){ 94 | return 'Length: min 4, max 80. Allowed: A-Z, a-z, 0-9, ~ @ # % ^ & * ( ) - _ = + [ ] { } \\ | ; : \' " , . / ? !'; 95 | } 96 | 97 | /** 98 | * Tests the string against the allowed chars regex 99 | * 100 | * @public 101 | * @method validatePassword 102 | * @param {string} password - The string to test 103 | * @param {function} failureCallback - (optional), the function to call on a failure 104 | * @return {boolean} Returns true if string passes regex, false if not 105 | */ 106 | function validatePassword(password, failureCallback) { 107 | if (passwordRegex.test(password) && checkLength(password, 5, 16)) { 108 | return true; 109 | } else { 110 | if (failureCallback && typeof(failureCallback) === "function") { 111 | failureCallback(this.getPasswordAllowedChars()); 112 | } 113 | return false; 114 | } 115 | } 116 | 117 | /** 118 | * Returns the regex of allowed chars 119 | * 120 | * @public 121 | * @method getPasswordAllowedChars 122 | * @return {string} Returns a string with the allowed chars 123 | */ 124 | function getPasswordAllowedChars(){ 125 | return 'Length: min 5, max 16. Allowed: A-Z, a-z, 0-9, ~ @ # % ^ & * ( ) - _ = + [ ] { } \\ | ; : \' " , . < > / ? !'; 126 | } 127 | 128 | /** 129 | * Tests the string against the allowed chars regex 130 | * 131 | * @public 132 | * @method validateEmail 133 | * @param {string} email - The string to test 134 | * @param {function} failureCallback - (optional), the function to call on a failure 135 | * @return {boolean} Returns true if string passes regex, false if not 136 | */ 137 | function validateEmail(email, failureCallback) { 138 | if (emailRegex.test(email) && checkLength(email, 4, 80)) { 139 | return true; 140 | } else { 141 | if (failureCallback && typeof(failureCallback) === "function") { 142 | failureCallback(this.getEmailAllowedChars()); 143 | } 144 | return false; 145 | } 146 | } 147 | 148 | /** 149 | * Returns the regex of allowed chars 150 | * 151 | * @public 152 | * @method getEmailAllowedChars 153 | * @return {string} Returns a string with the allowed chars 154 | */ 155 | function getEmailAllowedChars(){ 156 | return 'Email must be in standard form: e.g. example@Usergrid.com'; 157 | } 158 | 159 | /** 160 | * Tests the string against the allowed chars regex 161 | * 162 | * @public 163 | * @method validatePath 164 | * @param {string} path - The string to test 165 | * @param {function} failureCallback - (optional), the function to call on a failure 166 | * @return {boolean} Returns true if string passes regex, false if not 167 | */ 168 | function validatePath(path, failureCallback) { 169 | if (pathRegex.test(path) && checkLength(path, 4, 80)) { 170 | return true; 171 | } else { 172 | if (failureCallback && typeof(failureCallback) === "function") { 173 | failureCallback(this.getPathAllowedChars()); 174 | } 175 | return false; 176 | } 177 | } 178 | 179 | /** 180 | * Returns the regex of allowed chars 181 | * 182 | * @public 183 | * @method getPathAllowedChars 184 | * @return {string} Returns a string with the allowed chars 185 | */ 186 | function getPathAllowedChars(){ 187 | return 'Length: min 4, max 80. Allowed: /, a-z, 0-9, dot, and dash'; 188 | } 189 | 190 | /** 191 | * Tests the string against the allowed chars regex 192 | * 193 | * @public 194 | * @method validateTitle 195 | * @param {string} title - The string to test 196 | * @param {function} failureCallback - (optional), the function to call on a failure 197 | * @return {boolean} Returns true if string passes regex, false if not 198 | */ 199 | function validateTitle(title, failureCallback) { 200 | if (titleRegex.test(title) && checkLength(title, 4, 80)) { 201 | return true; 202 | } else { 203 | if (failureCallback && typeof(failureCallback) === "function") { 204 | failureCallback(this.getTitleAllowedChars()); 205 | } 206 | return false; 207 | } 208 | } 209 | 210 | /** 211 | * Returns the regex of allowed chars 212 | * 213 | * @public 214 | * @method getTitleAllowedChars 215 | * @return {string} Returns a string with the allowed chars 216 | */ 217 | function getTitleAllowedChars(){ 218 | return 'Length: min 4, max 80. Allowed: space, A-Z, a-z, 0-9, dot, dash, /, !, and ?'; 219 | } 220 | 221 | /** 222 | * Tests if the string is the correct length 223 | * 224 | * @public 225 | * @method checkLength 226 | * @param {string} string - The string to test 227 | * @param {integer} min - the lower bound 228 | * @param {integer} max - the upper bound 229 | * @return {boolean} Returns true if string is correct length, false if not 230 | */ 231 | function checkLength(string, min, max) { 232 | if (string.length > max || string.length < min) { 233 | return false; 234 | } 235 | return true; 236 | } 237 | 238 | /** 239 | * Tests if the string is a uuid 240 | * 241 | * @public 242 | * @method isUUID 243 | * @param {string} uuid The string to test 244 | * @returns {Boolean} true if string is uuid 245 | */ 246 | function isUUID (uuid) { 247 | 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}$/; 248 | if (!uuid) return false; 249 | return uuidValueRegex.test(uuid); 250 | } 251 | 252 | return { 253 | validateUsername:validateUsername, 254 | getUsernameAllowedChars:getUsernameAllowedChars, 255 | validateName:validateName, 256 | getNameAllowedChars:getNameAllowedChars, 257 | validatePassword:validatePassword, 258 | getPasswordAllowedChars:getPasswordAllowedChars, 259 | validateEmail:validateEmail, 260 | getEmailAllowedChars:getEmailAllowedChars, 261 | validatePath:validatePath, 262 | getPathAllowedChars:getPathAllowedChars, 263 | validateTitle:validateTitle, 264 | getTitleAllowedChars:getTitleAllowedChars, 265 | isUUID:isUUID 266 | } 267 | })(); 268 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
    28 | Usergrid Javascript SDK 29 |
    30 |
    31 |
    32 |

    Javascript SDK

    33 | This SDK is designed to enable you to connect your apps to the Usergrid API. 34 |
    35 |
    36 |

    Read me

    37 | View the read me file for the SDK: 38 | https://github.com/usergrid/usergrid/blob/master/sdks/html5-javascript/README.md. 39 |
    40 |
    41 |

    API documentation

    42 | For more information on Usergrid, see the Apache docs: 43 | http://usergrid.apache.org/ 44 |
    45 |
    46 | 47 |

    Example Apps:

    48 |
    49 | All Calls example app 50 |
    51 | This app shows the basic method for making all call types against the API. It also shows how to log in an app user. 52 |
    53 |
    54 | Dogs example app 55 |
    56 | This app shows you how to use the Entity and Collection objects 57 |
    58 |
    59 | Facebook Login Example 60 |
    61 | This app shows you how to use Facebook to log into Usergrid. 62 |
    63 |
    64 | Tests for examples in readme file 65 |
    66 | This is a test script that runs all the sample code shown in the readme file. See the samples in action! 67 |
    68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /lib/Module.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | //noinspection ThisExpressionReferencesGlobalObjectJS 21 | (function (global) { 22 | var name = 'Module', 23 | short = '_m', 24 | _name = global[name], 25 | _short = (short !== undefined) ? global[short] : undefined; 26 | 27 | function Module() { 28 | /* put code in here */ 29 | 30 | } 31 | 32 | global[name] = Module; 33 | if (short !== undefined) { 34 | global[short] = Module; 35 | } 36 | global[name].global[name].noConflict = function () { 37 | if (_name) { 38 | global[name] = _name; 39 | } 40 | if (short !== undefined) { 41 | global[short] = _short; 42 | } 43 | return Module; 44 | }; 45 | if( typeof module !== "undefined" && ('exports' in module)){ 46 | module.exports = global[name] 47 | } 48 | return global[name]; 49 | }(this)); 50 | -------------------------------------------------------------------------------- /lib/Usergrid.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | //Hack around IE console.log 21 | window.console = window.console || {}; 22 | window.console.log = window.console.log || function() {}; 23 | 24 | 25 | function extend(subClass, superClass) { 26 | var F = function() {}; 27 | F.prototype = superClass.prototype; 28 | subClass.prototype = new F(); 29 | subClass.prototype.constructor = subClass; 30 | 31 | subClass.superclass = superClass.prototype; 32 | if (superClass.prototype.constructor == Object.prototype.constructor) { 33 | superClass.prototype.constructor = superClass; 34 | } 35 | return subClass; 36 | } 37 | 38 | function propCopy(from, to) { 39 | for (var prop in from) { 40 | if (from.hasOwnProperty(prop)) { 41 | if ("object" === typeof from[prop] && "object" === typeof to[prop]) { 42 | to[prop] = propCopy(from[prop], to[prop]); 43 | } else { 44 | to[prop] = from[prop]; 45 | } 46 | } 47 | } 48 | return to; 49 | } 50 | 51 | function NOOP() {} 52 | 53 | function isValidUrl(url) { 54 | if (!url) return false; 55 | var doc, base, anchor, isValid = false; 56 | try { 57 | doc = document.implementation.createHTMLDocument(''); 58 | base = doc.createElement('base'); 59 | base.href = base || window.lo; 60 | doc.head.appendChild(base); 61 | anchor = doc.createElement('a'); 62 | anchor.href = url; 63 | doc.body.appendChild(anchor); 64 | isValid = !(anchor.href === '') 65 | } catch (e) { 66 | console.error(e); 67 | } finally { 68 | doc.head.removeChild(base); 69 | doc.body.removeChild(anchor); 70 | base = null; 71 | anchor = null; 72 | doc = null; 73 | return isValid; 74 | } 75 | } 76 | 77 | /* 78 | * Tests if the string is a uuid 79 | * 80 | * @public 81 | * @method isUUID 82 | * @param {string} uuid The string to test 83 | * @returns {Boolean} true if string is uuid 84 | */ 85 | 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}$/; 86 | 87 | function isUUID(uuid) { 88 | return (!uuid) ? false : uuidValueRegex.test(uuid); 89 | } 90 | 91 | /* 92 | * method to encode the query string parameters 93 | * 94 | * @method encodeParams 95 | * @public 96 | * @params {object} params - an object of name value pairs that will be urlencoded 97 | * @return {string} Returns the encoded string 98 | */ 99 | function encodeParams(params) { 100 | var queryString; 101 | if (params && Object.keys(params)) { 102 | queryString = [].slice.call(arguments) 103 | .reduce(function(a, b) { 104 | return a.concat((b instanceof Array) ? b : [b]); 105 | }, []) 106 | .filter(function(c) { 107 | return "object" === typeof c 108 | }) 109 | .reduce(function(p, c) { 110 | (!(c instanceof Array)) ? p = p.concat(Object.keys(c).map(function(key) { 111 | return [key, c[key]] 112 | })) : p.push(c); 113 | return p; 114 | }, []) 115 | .reduce(function(p, c) { 116 | ((c.length === 2) ? p.push(c) : p = p.concat(c)); 117 | return p; 118 | }, []) 119 | .reduce(function(p, c) { 120 | (c[1] instanceof Array) ? c[1].forEach(function(v) { 121 | p.push([c[0], v]) 122 | }) : p.push(c); 123 | return p; 124 | }, []) 125 | .map(function(c) { 126 | c[1] = encodeURIComponent(c[1]); 127 | return c.join('=') 128 | }) 129 | .join('&'); 130 | } 131 | return queryString; 132 | } 133 | 134 | 135 | /* 136 | * method to determine whether or not the passed variable is a function 137 | * 138 | * @method isFunction 139 | * @public 140 | * @params {any} f - any variable 141 | * @return {boolean} Returns true or false 142 | */ 143 | function isFunction(f) { 144 | return (f && f !== null && typeof(f) === 'function'); 145 | } 146 | 147 | /* 148 | * a safe wrapper for executing a callback 149 | * 150 | * @method doCallback 151 | * @public 152 | * @params {Function} callback - the passed-in callback method 153 | * @params {Array} params - an array of arguments to pass to the callback 154 | * @params {Object} context - an optional calling context for the callback 155 | * @return Returns whatever would be returned by the callback. or false. 156 | */ 157 | function doCallback(callback, params, context) { 158 | var returnValue; 159 | if (isFunction(callback)) { 160 | if (!params) params = []; 161 | if (!context) context = this; 162 | params.push(context); 163 | //try { 164 | returnValue = callback.apply(context, params); 165 | /*} catch (ex) { 166 | if (console && console.error) { 167 | console.error("Callback error:", ex); 168 | } 169 | }*/ 170 | } 171 | return returnValue; 172 | } 173 | 174 | //noinspection ThisExpressionReferencesGlobalObjectJS 175 | (function(global) { 176 | var name = 'Usergrid', 177 | overwrittenName = global[name]; 178 | var VALID_REQUEST_METHODS = ['GET', 'POST', 'PUT', 'DELETE']; 179 | 180 | function Usergrid() { 181 | this.logger = new Logger(name); 182 | } 183 | 184 | Usergrid.isValidEndpoint = function(endpoint) { 185 | //TODO actually implement this 186 | return true; 187 | }; 188 | 189 | Usergrid.Request = function(method, endpoint, query_params, data, callback) { 190 | var p = new Promise(); 191 | /* 192 | Create a logger 193 | */ 194 | this.logger = new global.Logger("Usergrid.Request"); 195 | this.logger.time("process request " + method + " " + endpoint); 196 | /* 197 | Validate our input 198 | */ 199 | this.endpoint = endpoint + '?' + encodeParams(query_params); 200 | this.method = method.toUpperCase(); 201 | //this.query_params = query_params; 202 | this.data = ("object" === typeof data) ? JSON.stringify(data) : data; 203 | 204 | if (VALID_REQUEST_METHODS.indexOf(this.method) === -1) { 205 | throw new UsergridInvalidHTTPMethodError("invalid request method '" + this.method + "'"); 206 | } 207 | 208 | /* 209 | Prepare our request 210 | */ 211 | if (!isValidUrl(this.endpoint)) { 212 | this.logger.error(endpoint, this.endpoint, /^https:\/\//.test(endpoint)); 213 | throw new UsergridInvalidURIError("The provided endpoint is not valid: " + this.endpoint); 214 | } 215 | /* a callback to make the request */ 216 | var request = function() { 217 | return Ajax.request(this.method, this.endpoint, this.data) 218 | }.bind(this); 219 | /* a callback to process the response */ 220 | var response = function(err, request) { 221 | return new Usergrid.Response(err, request) 222 | }.bind(this); 223 | /* a callback to clean up and return data to the client */ 224 | var oncomplete = function(err, response) { 225 | p.done(err, response); 226 | this.logger.info("REQUEST", err, response); 227 | doCallback(callback, [err, response]); 228 | this.logger.timeEnd("process request " + method + " " + endpoint); 229 | }.bind(this); 230 | /* and a promise to chain them all together */ 231 | Promise.chain([request, response]).then(oncomplete); 232 | 233 | return p; 234 | }; 235 | //TODO more granular handling of statusCodes 236 | Usergrid.Response = function(err, response) { 237 | var p = new Promise(); 238 | var data = null; 239 | try { 240 | data = JSON.parse(response.responseText); 241 | } catch (e) { 242 | //this.logger.error("Error parsing response text: ",this.text); 243 | //this.logger.error("Caught error ", e.message); 244 | data = {} 245 | } 246 | Object.keys(data).forEach(function(key) { 247 | Object.defineProperty(this, key, { 248 | value: data[key], 249 | enumerable: true 250 | }); 251 | }.bind(this)); 252 | Object.defineProperty(this, "logger", { 253 | enumerable: false, 254 | configurable: false, 255 | writable: false, 256 | value: new global.Logger(name) 257 | }); 258 | Object.defineProperty(this, "success", { 259 | enumerable: false, 260 | configurable: false, 261 | writable: true, 262 | value: true 263 | }); 264 | Object.defineProperty(this, "err", { 265 | enumerable: false, 266 | configurable: false, 267 | writable: true, 268 | value: err 269 | }); 270 | Object.defineProperty(this, "status", { 271 | enumerable: false, 272 | configurable: false, 273 | writable: true, 274 | value: parseInt(response.status) 275 | }); 276 | Object.defineProperty(this, "statusGroup", { 277 | enumerable: false, 278 | configurable: false, 279 | writable: true, 280 | value: (this.status - this.status % 100) 281 | }); 282 | switch (this.statusGroup) { 283 | case 200: //success 284 | this.success = true; 285 | break; 286 | case 400: //user error 287 | case 500: //server error 288 | case 300: //cache and redirects 289 | case 100: //upgrade 290 | default: 291 | //server error 292 | this.success = false; 293 | break; 294 | } 295 | if (this.success) { 296 | p.done(null, this); 297 | } else { 298 | p.done(UsergridError.fromResponse(data), this); 299 | } 300 | return p; 301 | }; 302 | Usergrid.Response.prototype.getEntities = function() { 303 | var entities; 304 | if (this.success) { 305 | entities = (this.data) ? this.data.entities : this.entities; 306 | } 307 | return entities || []; 308 | } 309 | Usergrid.Response.prototype.getEntity = function() { 310 | var entities = this.getEntities(); 311 | return entities[0]; 312 | } 313 | Usergrid.VERSION = Usergrid.USERGRID_SDK_VERSION = '0.11.0'; 314 | 315 | global[name] = Usergrid; 316 | global[name].noConflict = function() { 317 | if (overwrittenName) { 318 | global[name] = overwrittenName; 319 | } 320 | return Usergrid; 321 | }; 322 | return global[name]; 323 | }(this)); 324 | -------------------------------------------------------------------------------- /lib/modules/Asset.js: -------------------------------------------------------------------------------- 1 | /* 2 | *Licensed to the Apache Software Foundation (ASF) under one 3 | *or more contributor license agreements. See the NOTICE file 4 | *distributed with this work for additional information 5 | *regarding copyright ownership. The ASF licenses this file 6 | *to you under the Apache License, Version 2.0 (the 7 | *"License"); you may not use this file except in compliance 8 | *with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | *Unless required by applicable law or agreed to in writing, 13 | *software distributed under the License is distributed on an 14 | *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | *KIND, either express or implied. See the License for the 16 | *specific language governing permissions and limitations 17 | *under the License. 18 | */ 19 | 20 | 21 | 22 | /* 23 | * XMLHttpRequest.prototype.sendAsBinary polyfill 24 | * from: https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#sendAsBinary() 25 | * 26 | * @method sendAsBinary 27 | * @param {string} sData 28 | */ 29 | if (!XMLHttpRequest.prototype.sendAsBinary) { 30 | XMLHttpRequest.prototype.sendAsBinary = function(sData) { 31 | var nBytes = sData.length, 32 | ui8Data = new Uint8Array(nBytes); 33 | for (var nIdx = 0; nIdx < nBytes; nIdx++) { 34 | ui8Data[nIdx] = sData.charCodeAt(nIdx) & 0xff; 35 | } 36 | this.send(ui8Data); 37 | }; 38 | } 39 | 40 | 41 | /* 42 | * A class to model a Usergrid asset. 43 | * 44 | * @constructor 45 | * @param {object} options {name:"photo.jpg", path:"/user/uploads", "content-type":"image/jpeg", owner:"F01DE600-0000-0000-0000-000000000000" } 46 | * @returns {callback} callback(err, asset) 47 | */ 48 | Usergrid.Asset = function(options, callback) { 49 | var self = this, 50 | messages = []; 51 | self._client = options.client; 52 | self._data = options.data || {}; 53 | self._data.type = "assets"; 54 | var missingData = ["name", "owner", "path"].some(function(required) { 55 | return !(required in self._data); 56 | }); 57 | if (missingData) { 58 | doCallback(callback, [new UsergridError("Invalid asset data: 'name', 'owner', and 'path' are required properties."), null, self], self); 59 | } else { 60 | self.save(function(err, data) { 61 | if (err) { 62 | doCallback(callback, [new UsergridError(data), data, self], self); 63 | } else { 64 | if (data && data.entities && data.entities.length) { 65 | self.set(data.entities[0]); 66 | } 67 | doCallback(callback, [null, data, self], self); 68 | } 69 | }); 70 | } 71 | }; 72 | 73 | /* 74 | * Inherit from Usergrid.Entity. 75 | */ 76 | Usergrid.Asset.prototype = new Usergrid.Entity(); 77 | 78 | /* 79 | * Add an asset to a folder. 80 | * 81 | * @method connect 82 | * @public 83 | * @param {object} options {folder:"F01DE600-0000-0000-0000-000000000000"} 84 | * @returns {callback} callback(err, asset) 85 | */ 86 | 87 | Usergrid.Asset.prototype.addToFolder = function(options, callback) { 88 | var self = this, 89 | error = null; 90 | if (('folder' in options) && isUUID(options.folder)) { 91 | //we got a valid UUID 92 | var folder = Usergrid.Folder({ 93 | uuid: options.folder 94 | }, function(err, folder) { 95 | if (err) { 96 | doCallback(callback, [UsergridError.fromResponse(folder), folder, self], self); 97 | } else { 98 | var endpoint = ["folders", folder.get("uuid"), "assets", self.get("uuid")].join('/'); 99 | var options = { 100 | method: 'POST', 101 | endpoint: endpoint 102 | }; 103 | this._client.request(options, function(err, response) { 104 | if (err) { 105 | doCallback(callback, [UsergridError.fromResponse(folder), response, self], self); 106 | } else { 107 | doCallback(callback, [null, folder, self], self); 108 | } 109 | 110 | 111 | }); 112 | } 113 | }); 114 | } else { 115 | doCallback(callback, [new UsergridError('folder not specified'), null, self], self); 116 | } 117 | }; 118 | 119 | Usergrid.Entity.prototype.attachAsset = function (file, callback) { 120 | 121 | if (!(window.File && window.FileReader && window.FileList && window.Blob)) { 122 | doCallback(callback, [ new UsergridError("The File APIs are not fully supported by your browser."), null, this ], this); 123 | return; 124 | } 125 | var self = this; 126 | var args = arguments; 127 | var type = this._data.type; 128 | var attempts = self.get("attempts"); 129 | if (isNaN(attempts)) { 130 | attempts = 3; 131 | } 132 | 133 | if(type != 'assets' && type != 'asset') { 134 | var endpoint = [ this._client.URI, this._client.orgName, this._client.appName, type, self.get("uuid") ].join("/"); 135 | } else { 136 | self.set("content-type", file.type); 137 | self.set("size", file.size); 138 | var endpoint = [ this._client.URI, this._client.orgName, this._client.appName, "assets", self.get("uuid"), "data" ].join("/"); 139 | } 140 | 141 | var xhr = new XMLHttpRequest(); 142 | xhr.open("POST", endpoint, true); 143 | xhr.onerror = function(err) { 144 | doCallback(callback, [ new UsergridError("The File APIs are not fully supported by your browser.") ], xhr, self); 145 | }; 146 | xhr.onload = function(ev) { 147 | if (xhr.status >= 500 && attempts > 0) { 148 | self.set("attempts", --attempts); 149 | setTimeout(function() { 150 | self.attachAsset.apply(self, args); 151 | }, 100); 152 | } else if (xhr.status >= 300) { 153 | self.set("attempts"); 154 | doCallback(callback, [ new UsergridError(JSON.parse(xhr.responseText)), xhr, self ], self); 155 | } else { 156 | self.set("attempts"); 157 | self.fetch(); 158 | doCallback(callback, [ null, xhr, self ], self); 159 | } 160 | }; 161 | var fr = new FileReader(); 162 | fr.onload = function() { 163 | var binary = fr.result; 164 | if (type === 'assets' || type === 'asset') { 165 | xhr.overrideMimeType("application/octet-stream"); 166 | xhr.setRequestHeader("Content-Type", "application/octet-stream"); 167 | } 168 | xhr.sendAsBinary(binary); 169 | }; 170 | fr.readAsBinaryString(file); 171 | 172 | }; 173 | 174 | /* 175 | * Upload Asset data 176 | * 177 | * @method upload 178 | * @public 179 | * @param {object} data Can be a javascript Blob or File object 180 | * @returns {callback} callback(err, asset) 181 | */ 182 | Usergrid.Asset.prototype.upload = function(data, callback) { 183 | this.attachAsset(data, function(err, response) { 184 | if(!err){ 185 | doCallback(callback, [ null, response, self ], self); 186 | } else { 187 | doCallback(callback, [ new UsergridError(err), response, self ], self); 188 | } 189 | }); 190 | }; 191 | 192 | /* 193 | * Download Asset data 194 | * 195 | * @method download 196 | * @public 197 | * @returns {callback} callback(err, blob) blob is a javascript Blob object. 198 | */ 199 | Usergrid.Entity.prototype.downloadAsset = function(callback) { 200 | var self = this; 201 | var endpoint; 202 | var type = this._data.type; 203 | 204 | var xhr = new XMLHttpRequest(); 205 | if(type != "assets" && type != 'asset') { 206 | endpoint = [ this._client.URI, this._client.orgName, this._client.appName, type, self.get("uuid") ].join("/"); 207 | } else { 208 | endpoint = [ this._client.URI, this._client.orgName, this._client.appName, "assets", self.get("uuid"), "data" ].join("/"); 209 | } 210 | xhr.open("GET", endpoint, true); 211 | xhr.responseType = "blob"; 212 | xhr.onload = function(ev) { 213 | var blob = xhr.response; 214 | if(type != "assets" && type != 'asset') { 215 | doCallback(callback, [ null, blob, xhr ], self); 216 | } else { 217 | doCallback(callback, [ null, xhr, self ], self); 218 | } 219 | }; 220 | xhr.onerror = function(err) { 221 | callback(true, err); 222 | doCallback(callback, [ new UsergridError(err), xhr, self ], self); 223 | }; 224 | 225 | if(type != "assets" && type != 'asset') { 226 | xhr.setRequestHeader("Accept", self._data["file-metadata"]["content-type"]); 227 | } else { 228 | xhr.overrideMimeType(self.get("content-type")); 229 | } 230 | xhr.send(); 231 | }; 232 | 233 | /* 234 | * Download Asset data 235 | * 236 | * @method download 237 | * @public 238 | * @returns {callback} callback(err, blob) blob is a javascript Blob object. 239 | */ 240 | Usergrid.Asset.prototype.download = function(callback) { 241 | this.downloadAsset(function(err, response) { 242 | if(!err){ 243 | doCallback(callback, [ null, response, self ], self); 244 | } else { 245 | doCallback(callback, [ new UsergridError(err), response, self ], self); 246 | } 247 | }); 248 | }; -------------------------------------------------------------------------------- /lib/modules/Collection.js: -------------------------------------------------------------------------------- 1 | /* 2 | *Licensed to the Apache Software Foundation (ASF) under one 3 | *or more contributor license agreements. See the NOTICE file 4 | *distributed with this work for additional information 5 | *regarding copyright ownership. The ASF licenses this file 6 | *to you under the Apache License, Version 2.0 (the 7 | *"License"); you may not use this file except in compliance 8 | *with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | *Unless required by applicable law or agreed to in writing, 13 | *software distributed under the License is distributed on an 14 | *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | *KIND, either express or implied. See the License for the 16 | *specific language governing permissions and limitations 17 | *under the License. 18 | */ 19 | 20 | 21 | /* 22 | * The Collection class models Usergrid Collections. It essentially 23 | * acts as a container for holding Entity objects, while providing 24 | * additional funcitonality such as paging, and saving 25 | * 26 | * @constructor 27 | * @param {string} options - configuration object 28 | * @return {Collection} collection 29 | */ 30 | Usergrid.Collection = function(options) { 31 | 32 | if (options) { 33 | this._client = options.client; 34 | this._type = options.type; 35 | this.qs = options.qs || {}; 36 | 37 | //iteration 38 | this._list = options.list || []; 39 | this._iterator = options.iterator || -1; //first thing we do is increment, so set to -1 40 | 41 | //paging 42 | this._previous = options.previous || []; 43 | this._next = options.next || null; 44 | this._cursor = options.cursor || null; 45 | 46 | //restore entities if available 47 | if (options.list) { 48 | var count = options.list.length; 49 | for (var i = 0; i < count; i++) { 50 | //make new entity with 51 | var entity = this._client.restoreEntity(options.list[i]); 52 | this._list[i] = entity; 53 | } 54 | } 55 | } 56 | /*if (callback) { 57 | //populate the collection 58 | this.fetch(callback); 59 | }*/ 60 | 61 | }; 62 | 63 | 64 | /* 65 | * method to determine whether or not the passed variable is a Usergrid Collection 66 | * 67 | * @method isCollection 68 | * @public 69 | * @params {any} obj - any variable 70 | * @return {boolean} Returns true or false 71 | */ 72 | Usergrid.isCollection = function(obj) { 73 | return (obj && obj instanceof Usergrid.Collection); 74 | }; 75 | 76 | 77 | /* 78 | * gets the data from the collection object for serialization 79 | * 80 | * @method serialize 81 | * @return {object} data 82 | */ 83 | Usergrid.Collection.prototype.serialize = function() { 84 | 85 | //pull out the state from this object and return it 86 | var data = {}; 87 | data.type = this._type; 88 | data.qs = this.qs; 89 | data.iterator = this._iterator; 90 | data.previous = this._previous; 91 | data.next = this._next; 92 | data.cursor = this._cursor; 93 | 94 | this.resetEntityPointer(); 95 | var i = 0; 96 | data.list = []; 97 | while (this.hasNextEntity()) { 98 | var entity = this.getNextEntity(); 99 | data.list[i] = entity.serialize(); 100 | i++; 101 | } 102 | 103 | data = JSON.stringify(data); 104 | return data; 105 | }; 106 | //addCollection is deprecated? 107 | /*Usergrid.Collection.prototype.addCollection = function (collectionName, options, callback) { 108 | self = this; 109 | options.client = this._client; 110 | var collection = new Usergrid.Collection(options, function(err, data) { 111 | if (typeof(callback) === 'function') { 112 | 113 | collection.resetEntityPointer(); 114 | while(collection.hasNextEntity()) { 115 | var user = collection.getNextEntity(); 116 | var email = user.get('email'); 117 | var image = self._client.getDisplayImage(user.get('email'), user.get('picture')); 118 | user._portal_image_icon = image; 119 | } 120 | 121 | self[collectionName] = collection; 122 | doCallback(callback, [err, collection], self); 123 | } 124 | }); 125 | };*/ 126 | 127 | /* 128 | * Populates the collection from the server 129 | * 130 | * @method fetch 131 | * @param {function} callback 132 | * @return {callback} callback(err, data) 133 | */ 134 | Usergrid.Collection.prototype.fetch = function(callback) { 135 | var self = this; 136 | var qs = this.qs; 137 | 138 | //add in the cursor if one is available 139 | if (this._cursor) { 140 | qs.cursor = this._cursor; 141 | } else { 142 | delete qs.cursor; 143 | } 144 | var options = { 145 | method: 'GET', 146 | endpoint: this._type, 147 | qs: this.qs 148 | }; 149 | this._client.request(options, function(err, response) { 150 | if (err && self._client.logging) { 151 | console.log('error getting collection'); 152 | } else { 153 | //save the cursor if there is one 154 | self.saveCursor(response.cursor || null); 155 | self.resetEntityPointer(); 156 | //save entities locally 157 | self._list = response.getEntities() 158 | .filter(function(entity) { 159 | return isUUID(entity.uuid); 160 | }) 161 | .map(function(entity) { 162 | var ent = new Usergrid.Entity({ 163 | client: self._client 164 | }); 165 | ent.set(entity); 166 | ent.type = self._type; 167 | //ent._json = JSON.stringify(entity, null, 2); 168 | return ent; 169 | }); 170 | } 171 | doCallback(callback, [err, response, self], self); 172 | }); 173 | }; 174 | 175 | /* 176 | * Adds a new Entity to the collection (saves, then adds to the local object) 177 | * 178 | * @method addNewEntity 179 | * @param {object} entity 180 | * @param {function} callback 181 | * @return {callback} callback(err, data, entity) 182 | */ 183 | Usergrid.Collection.prototype.addEntity = function(entityObject, callback) { 184 | var self = this; 185 | entityObject.type = this._type; 186 | 187 | //create the new entity 188 | this._client.createEntity(entityObject, function(err, response, entity) { 189 | if (!err) { 190 | //then add the entity to the list 191 | self.addExistingEntity(entity); 192 | } 193 | doCallback(callback, [err, response, self], self); 194 | }); 195 | }; 196 | 197 | Usergrid.Collection.prototype.addExistingEntity = function(entity) { 198 | //entity should already exist in the db, so just add it to the list 199 | var count = this._list.length; 200 | this._list[count] = entity; 201 | }; 202 | 203 | /* 204 | * Removes the Entity from the collection, then destroys the object on the server 205 | * 206 | * @method destroyEntity 207 | * @param {object} entity 208 | * @param {function} callback 209 | * @return {callback} callback(err, data) 210 | */ 211 | Usergrid.Collection.prototype.destroyEntity = function(entity, callback) { 212 | var self = this; 213 | entity.destroy(function(err, response) { 214 | if (err) { 215 | if (self._client.logging) { 216 | console.log('could not destroy entity'); 217 | } 218 | doCallback(callback, [err, response, self], self); 219 | } else { 220 | //destroy was good, so repopulate the collection 221 | self.fetch(callback); 222 | } 223 | //remove entity from the local store 224 | self.removeEntity(entity); 225 | }); 226 | }; 227 | 228 | /* 229 | * Filters the list of entities based on the supplied criteria function 230 | * works like Array.prototype.filter 231 | * 232 | * @method getEntitiesByCriteria 233 | * @param {function} criteria A function that takes each entity as an argument and returns true or false 234 | * @return {Entity[]} returns a list of entities that caused the criteria function to return true 235 | */ 236 | Usergrid.Collection.prototype.getEntitiesByCriteria = function(criteria) { 237 | return this._list.filter(criteria); 238 | }; 239 | /* 240 | * Returns the first entity from the list of entities based on the supplied criteria function 241 | * works like Array.prototype.filter 242 | * 243 | * @method getEntitiesByCriteria 244 | * @param {function} criteria A function that takes each entity as an argument and returns true or false 245 | * @return {Entity[]} returns a list of entities that caused the criteria function to return true 246 | */ 247 | Usergrid.Collection.prototype.getEntityByCriteria = function(criteria) { 248 | return this.getEntitiesByCriteria(criteria).shift(); 249 | }; 250 | /* 251 | * Removed an entity from the collection without destroying it on the server 252 | * 253 | * @method removeEntity 254 | * @param {object} entity 255 | * @return {Entity} returns the removed entity or undefined if it was not found 256 | */ 257 | Usergrid.Collection.prototype.removeEntity = function(entity) { 258 | var removedEntity = this.getEntityByCriteria(function(item) { 259 | return entity.uuid === item.get('uuid'); 260 | }); 261 | delete this._list[this._list.indexOf(removedEntity)]; 262 | return removedEntity; 263 | }; 264 | 265 | /* 266 | * Looks up an Entity by UUID 267 | * 268 | * @method getEntityByUUID 269 | * @param {string} UUID 270 | * @param {function} callback 271 | * @return {callback} callback(err, data, entity) 272 | */ 273 | Usergrid.Collection.prototype.getEntityByUUID = function(uuid, callback) { 274 | var entity = this.getEntityByCriteria(function(item) { 275 | return item.get('uuid') === uuid; 276 | }); 277 | if (entity) { 278 | doCallback(callback, [null, entity, entity], this); 279 | } else { 280 | //get the entity from the database 281 | var options = { 282 | data: { 283 | type: this._type, 284 | uuid: uuid 285 | }, 286 | client: this._client 287 | }; 288 | entity = new Usergrid.Entity(options); 289 | entity.fetch(callback); 290 | } 291 | }; 292 | 293 | /* 294 | * Returns the first Entity of the Entity list - does not affect the iterator 295 | * 296 | * @method getFirstEntity 297 | * @return {object} returns an entity object 298 | */ 299 | Usergrid.Collection.prototype.getFirstEntity = function() { 300 | var count = this._list.length; 301 | if (count > 0) { 302 | return this._list[0]; 303 | } 304 | return null; 305 | }; 306 | 307 | /* 308 | * Returns the last Entity of the Entity list - does not affect the iterator 309 | * 310 | * @method getLastEntity 311 | * @return {object} returns an entity object 312 | */ 313 | Usergrid.Collection.prototype.getLastEntity = function() { 314 | var count = this._list.length; 315 | if (count > 0) { 316 | return this._list[count - 1]; 317 | } 318 | return null; 319 | }; 320 | 321 | /* 322 | * Entity iteration -Checks to see if there is a "next" entity 323 | * in the list. The first time this method is called on an entity 324 | * list, or after the resetEntityPointer method is called, it will 325 | * return true referencing the first entity in the list 326 | * 327 | * @method hasNextEntity 328 | * @return {boolean} true if there is a next entity, false if not 329 | */ 330 | Usergrid.Collection.prototype.hasNextEntity = function() { 331 | var next = this._iterator + 1; 332 | var hasNextElement = (next >= 0 && next < this._list.length); 333 | if (hasNextElement) { 334 | return true; 335 | } 336 | return false; 337 | }; 338 | 339 | /* 340 | * Entity iteration - Gets the "next" entity in the list. The first 341 | * time this method is called on an entity list, or after the method 342 | * resetEntityPointer is called, it will return the, 343 | * first entity in the list 344 | * 345 | * @method hasNextEntity 346 | * @return {object} entity 347 | */ 348 | Usergrid.Collection.prototype.getNextEntity = function() { 349 | this._iterator++; 350 | var hasNextElement = (this._iterator >= 0 && this._iterator <= this._list.length); 351 | if (hasNextElement) { 352 | return this._list[this._iterator]; 353 | } 354 | return false; 355 | }; 356 | 357 | /* 358 | * Entity iteration - Checks to see if there is a "previous" 359 | * entity in the list. 360 | * 361 | * @method hasPrevEntity 362 | * @return {boolean} true if there is a previous entity, false if not 363 | */ 364 | Usergrid.Collection.prototype.hasPrevEntity = function() { 365 | var previous = this._iterator - 1; 366 | var hasPreviousElement = (previous >= 0 && previous < this._list.length); 367 | if (hasPreviousElement) { 368 | return true; 369 | } 370 | return false; 371 | }; 372 | 373 | /* 374 | * Entity iteration - Gets the "previous" entity in the list. 375 | * 376 | * @method getPrevEntity 377 | * @return {object} entity 378 | */ 379 | Usergrid.Collection.prototype.getPrevEntity = function() { 380 | this._iterator--; 381 | var hasPreviousElement = (this._iterator >= 0 && this._iterator <= this._list.length); 382 | if (hasPreviousElement) { 383 | return this._list[this._iterator]; 384 | } 385 | return false; 386 | }; 387 | 388 | /* 389 | * Entity iteration - Resets the iterator back to the beginning 390 | * of the list 391 | * 392 | * @method resetEntityPointer 393 | * @return none 394 | */ 395 | Usergrid.Collection.prototype.resetEntityPointer = function() { 396 | this._iterator = -1; 397 | }; 398 | 399 | /* 400 | * Method to save off the cursor just returned by the last API call 401 | * 402 | * @public 403 | * @method saveCursor 404 | * @return none 405 | */ 406 | Usergrid.Collection.prototype.saveCursor = function(cursor) { 407 | //if current cursor is different, grab it for next cursor 408 | if (this._next !== cursor) { 409 | this._next = cursor; 410 | } 411 | }; 412 | 413 | /* 414 | * Resets the paging pointer (back to original page) 415 | * 416 | * @public 417 | * @method resetPaging 418 | * @return none 419 | */ 420 | Usergrid.Collection.prototype.resetPaging = function() { 421 | this._previous = []; 422 | this._next = null; 423 | this._cursor = null; 424 | }; 425 | 426 | /* 427 | * Paging - checks to see if there is a next page od data 428 | * 429 | * @method hasNextPage 430 | * @return {boolean} returns true if there is a next page of data, false otherwise 431 | */ 432 | Usergrid.Collection.prototype.hasNextPage = function() { 433 | return (this._next); 434 | }; 435 | 436 | /* 437 | * Paging - advances the cursor and gets the next 438 | * page of data from the API. Stores returned entities 439 | * in the Entity list. 440 | * 441 | * @method getNextPage 442 | * @param {function} callback 443 | * @return {callback} callback(err, data) 444 | */ 445 | Usergrid.Collection.prototype.getNextPage = function(callback) { 446 | if (this.hasNextPage()) { 447 | //set the cursor to the next page of data 448 | this._previous.push(this._cursor); 449 | this._cursor = this._next; 450 | //empty the list 451 | this._list = []; 452 | this.fetch(callback); 453 | } 454 | }; 455 | 456 | /* 457 | * Paging - checks to see if there is a previous page od data 458 | * 459 | * @method hasPreviousPage 460 | * @return {boolean} returns true if there is a previous page of data, false otherwise 461 | */ 462 | Usergrid.Collection.prototype.hasPreviousPage = function() { 463 | return (this._previous.length > 0); 464 | }; 465 | 466 | /* 467 | * Paging - reverts the cursor and gets the previous 468 | * page of data from the API. Stores returned entities 469 | * in the Entity list. 470 | * 471 | * @method getPreviousPage 472 | * @param {function} callback 473 | * @return {callback} callback(err, data) 474 | */ 475 | Usergrid.Collection.prototype.getPreviousPage = function(callback) { 476 | if (this.hasPreviousPage()) { 477 | this._next = null; //clear out next so the comparison will find the next item 478 | this._cursor = this._previous.pop(); 479 | //empty the list 480 | this._list = []; 481 | this.fetch(callback); 482 | } 483 | }; 484 | -------------------------------------------------------------------------------- /lib/modules/Counter.js: -------------------------------------------------------------------------------- 1 | /* 2 | *Licensed to the Apache Software Foundation (ASF) under one 3 | *or more contributor license agreements. See the NOTICE file 4 | *distributed with this work for additional information 5 | *regarding copyright ownership. The ASF licenses this file 6 | *to you under the Apache License, Version 2.0 (the 7 | *"License"); you may not use this file except in compliance 8 | *with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | *Unless required by applicable law or agreed to in writing, 13 | *software distributed under the License is distributed on an 14 | *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | *KIND, either express or implied. See the License for the 16 | *specific language governing permissions and limitations 17 | *under the License. 18 | */ 19 | 20 | 21 | /* 22 | * A class to model a Usergrid event. 23 | * 24 | * @constructor 25 | * @param {object} options {timestamp:0, category:'value', counters:{name : value}} 26 | * @returns {callback} callback(err, event) 27 | */ 28 | Usergrid.Counter = function(options) { 29 | // var self=this; 30 | this._client = options.client; 31 | this._data = options.data || {}; 32 | this._data.category = options.category || "UNKNOWN"; 33 | this._data.timestamp = options.timestamp || 0; 34 | this._data.type = "events"; 35 | this._data.counters = options.counters || {}; 36 | // doCallback(callback, [false, this], this); 37 | //this.save(callback); 38 | }; 39 | var COUNTER_RESOLUTIONS = [ 40 | 'all', 'minute', 'five_minutes', 'half_hour', 41 | 'hour', 'six_day', 'day', 'week', 'month' 42 | ]; 43 | /* 44 | * Inherit from Usergrid.Entity. 45 | * Note: This only accounts for data on the group object itself. 46 | * You need to use add and remove to manipulate group membership. 47 | */ 48 | Usergrid.Counter.prototype = new Usergrid.Entity(); 49 | 50 | /* 51 | * overrides Entity.prototype.fetch. Returns all data for counters 52 | * associated with the object as specified in the constructor 53 | * 54 | * @public 55 | * @method increment 56 | * @param {function} callback 57 | * @returns {callback} callback(err, event) 58 | */ 59 | Usergrid.Counter.prototype.fetch = function(callback) { 60 | this.getData({}, callback); 61 | }; 62 | /* 63 | * increments the counter for a specific event 64 | * 65 | * options object: {name: counter_name} 66 | * 67 | * @public 68 | * @method increment 69 | * @params {object} options 70 | * @param {function} callback 71 | * @returns {callback} callback(err, event) 72 | */ 73 | Usergrid.Counter.prototype.increment = function(options, callback) { 74 | var self = this, 75 | name = options.name, 76 | value = options.value; 77 | if (!name) { 78 | return doCallback(callback, [new UsergridInvalidArgumentError("'name' for increment, decrement must be a number"), null, self], self); 79 | } else if (isNaN(value)) { 80 | return doCallback(callback, [new UsergridInvalidArgumentError("'value' for increment, decrement must be a number"), null, self], self); 81 | } else { 82 | self._data.counters[name] = (parseInt(value)) || 1; 83 | return self.save(callback); 84 | } 85 | }; 86 | /* 87 | * decrements the counter for a specific event 88 | * 89 | * options object: {name: counter_name} 90 | * 91 | * @public 92 | * @method decrement 93 | * @params {object} options 94 | * @param {function} callback 95 | * @returns {callback} callback(err, event) 96 | */ 97 | 98 | Usergrid.Counter.prototype.decrement = function(options, callback) { 99 | var self = this, 100 | name = options.name, 101 | value = options.value; 102 | self.increment({ 103 | name: name, 104 | value: -((parseInt(value)) || 1) 105 | }, callback); 106 | }; 107 | /* 108 | * resets the counter for a specific event 109 | * 110 | * options object: {name: counter_name} 111 | * 112 | * @public 113 | * @method reset 114 | * @params {object} options 115 | * @param {function} callback 116 | * @returns {callback} callback(err, event) 117 | */ 118 | 119 | Usergrid.Counter.prototype.reset = function(options, callback) { 120 | var self = this, 121 | name = options.name; 122 | self.increment({ 123 | name: name, 124 | value: 0 125 | }, callback); 126 | }; 127 | 128 | /* 129 | * gets data for one or more counters over a given 130 | * time period at a specified resolution 131 | * 132 | * options object: { 133 | * counters: ['counter1', 'counter2', ...], 134 | * start: epoch timestamp or ISO date string, 135 | * end: epoch timestamp or ISO date string, 136 | * resolution: one of ('all', 'minute', 'five_minutes', 'half_hour', 'hour', 'six_day', 'day', 'week', or 'month') 137 | * } 138 | * 139 | * @public 140 | * @method getData 141 | * @params {object} options 142 | * @param {function} callback 143 | * @returns {callback} callback(err, event) 144 | */ 145 | Usergrid.Counter.prototype.getData = function(options, callback) { 146 | var start_time, 147 | end_time, 148 | start = options.start || 0, 149 | end = options.end || Date.now(), 150 | resolution = (options.resolution || 'all').toLowerCase(), 151 | counters = options.counters || Object.keys(this._data.counters), 152 | res = (resolution || 'all').toLowerCase(); 153 | if (COUNTER_RESOLUTIONS.indexOf(res) === -1) { 154 | res = 'all'; 155 | } 156 | start_time = getSafeTime(start); 157 | end_time = getSafeTime(end); 158 | var self = this; 159 | //https://api.usergrid.com/yourorgname/sandbox/counters?counter=test_counter 160 | var params = Object.keys(counters).map(function(counter) { 161 | return ["counter", encodeURIComponent(counters[counter])].join('='); 162 | }); 163 | params.push('resolution=' + res); 164 | params.push('start_time=' + String(start_time)); 165 | params.push('end_time=' + String(end_time)); 166 | 167 | var endpoint = "counters?" + params.join('&'); 168 | this._client.request({ 169 | endpoint: endpoint 170 | }, function(err, data) { 171 | if (data.counters && data.counters.length) { 172 | data.counters.forEach(function(counter) { 173 | self._data.counters[counter.name] = counter.value || counter.values; 174 | }); 175 | } 176 | return doCallback(callback, [err, data, self], self); 177 | }); 178 | }; 179 | 180 | function getSafeTime(prop) { 181 | var time; 182 | switch (typeof prop) { 183 | case "undefined": 184 | time = Date.now(); 185 | break; 186 | case "number": 187 | time = prop; 188 | break; 189 | case "string": 190 | time = (isNaN(prop)) ? Date.parse(prop) : parseInt(prop); 191 | break; 192 | default: 193 | time = Date.parse(prop.toString()); 194 | } 195 | return time; 196 | } 197 | -------------------------------------------------------------------------------- /lib/modules/Error.js: -------------------------------------------------------------------------------- 1 | /* 2 | *Licensed to the Apache Software Foundation (ASF) under one 3 | *or more contributor license agreements. See the NOTICE file 4 | *distributed with this work for additional information 5 | *regarding copyright ownership. The ASF licenses this file 6 | *to you under the Apache License, Version 2.0 (the 7 | *"License"); you may not use this file except in compliance 8 | *with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | *Unless required by applicable law or agreed to in writing, 13 | *software distributed under the License is distributed on an 14 | *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | *KIND, either express or implied. See the License for the 16 | *specific language governing permissions and limitations 17 | *under the License. 18 | */ 19 | //noinspection ThisExpressionReferencesGlobalObjectJS 20 | 21 | /** 22 | * Created by ryan bridges on 2014-02-05. 23 | */ 24 | (function(global) { 25 | //noinspection JSUnusedAssignment 26 | var name = 'UsergridError', 27 | short, 28 | _name = global[name], 29 | _short = (short && short !== undefined) ? global[short] : undefined; 30 | 31 | /* 32 | * Instantiates a new UsergridError 33 | * 34 | * @method UsergridError 35 | * @public 36 | * @params {} message 37 | * @params {} id - the error code, id, or name 38 | * @params {} timestamp 39 | * @params {} duration 40 | * @params {} exception - the Java exception from Usergrid 41 | * @return Returns - a new UsergridError object 42 | * 43 | * Example: 44 | * 45 | * UsergridError(message); 46 | */ 47 | 48 | function UsergridError(message, name, timestamp, duration, exception) { 49 | this.message = message; 50 | this.name = name; 51 | this.timestamp = timestamp || Date.now(); 52 | this.duration = duration || 0; 53 | this.exception = exception; 54 | } 55 | UsergridError.prototype = new Error(); 56 | UsergridError.prototype.constructor = UsergridError; 57 | /* 58 | * Creates a UsergridError from the JSON response returned from the backend 59 | * 60 | * @method fromResponse 61 | * @public 62 | * @params {object} response - the deserialized HTTP response from the Usergrid API 63 | * @return Returns a new UsergridError object. 64 | * 65 | * Example: 66 | * { 67 | * "error":"organization_application_not_found", 68 | * "timestamp":1391618508079, 69 | * "duration":0, 70 | * "exception":"org.usergrid.rest.exceptions.OrganizationApplicationNotFoundException", 71 | * "error_description":"Could not find application for yourorgname/sandboxxxxx from URI: yourorgname/sandboxxxxx" 72 | * } 73 | */ 74 | UsergridError.fromResponse = function(response) { 75 | if (response && "undefined" !== typeof response) { 76 | return new UsergridError(response.error_description, response.error, response.timestamp, response.duration, response.exception); 77 | } else { 78 | return new UsergridError(); 79 | } 80 | }; 81 | UsergridError.createSubClass = function(name) { 82 | if (name in global && global[name]) return global[name]; 83 | global[name] = function() {}; 84 | global[name].name = name; 85 | global[name].prototype = new UsergridError(); 86 | return global[name]; 87 | }; 88 | 89 | function UsergridHTTPResponseError(message, name, timestamp, duration, exception) { 90 | this.message = message; 91 | this.name = name; 92 | this.timestamp = timestamp || Date.now(); 93 | this.duration = duration || 0; 94 | this.exception = exception; 95 | } 96 | UsergridHTTPResponseError.prototype = new UsergridError(); 97 | 98 | function UsergridInvalidHTTPMethodError(message, name, timestamp, duration, exception) { 99 | this.message = message; 100 | this.name = name || 'invalid_http_method'; 101 | this.timestamp = timestamp || Date.now(); 102 | this.duration = duration || 0; 103 | this.exception = exception; 104 | } 105 | UsergridInvalidHTTPMethodError.prototype = new UsergridError(); 106 | 107 | function UsergridInvalidURIError(message, name, timestamp, duration, exception) { 108 | this.message = message; 109 | this.name = name || 'invalid_uri'; 110 | this.timestamp = timestamp || Date.now(); 111 | this.duration = duration || 0; 112 | this.exception = exception; 113 | } 114 | UsergridInvalidURIError.prototype = new UsergridError(); 115 | 116 | function UsergridInvalidArgumentError(message, name, timestamp, duration, exception) { 117 | this.message = message; 118 | this.name = name || 'invalid_argument'; 119 | this.timestamp = timestamp || Date.now(); 120 | this.duration = duration || 0; 121 | this.exception = exception; 122 | } 123 | UsergridInvalidArgumentError.prototype = new UsergridError(); 124 | 125 | function UsergridKeystoreDatabaseUpgradeNeededError(message, name, timestamp, duration, exception) { 126 | this.message = message; 127 | this.name = name; 128 | this.timestamp = timestamp || Date.now(); 129 | this.duration = duration || 0; 130 | this.exception = exception; 131 | } 132 | UsergridKeystoreDatabaseUpgradeNeededError.prototype = new UsergridError(); 133 | 134 | global.UsergridHTTPResponseError = UsergridHTTPResponseError; 135 | global.UsergridInvalidHTTPMethodError = UsergridInvalidHTTPMethodError; 136 | global.UsergridInvalidURIError = UsergridInvalidURIError; 137 | global.UsergridInvalidArgumentError = UsergridInvalidArgumentError; 138 | global.UsergridKeystoreDatabaseUpgradeNeededError = UsergridKeystoreDatabaseUpgradeNeededError; 139 | 140 | global[name] = UsergridError; 141 | if (short !== undefined) { 142 | //noinspection JSUnusedAssignment 143 | global[short] = UsergridError; 144 | } 145 | global[name].noConflict = function() { 146 | if (_name) { 147 | global[name] = _name; 148 | } 149 | if (short !== undefined) { 150 | global[short] = _short; 151 | } 152 | return UsergridError; 153 | }; 154 | return global[name]; 155 | }(this)); 156 | -------------------------------------------------------------------------------- /lib/modules/Folder.js: -------------------------------------------------------------------------------- 1 | /* 2 | *Licensed to the Apache Software Foundation (ASF) under one 3 | *or more contributor license agreements. See the NOTICE file 4 | *distributed with this work for additional information 5 | *regarding copyright ownership. The ASF licenses this file 6 | *to you under the Apache License, Version 2.0 (the 7 | *"License"); you may not use this file except in compliance 8 | *with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | *Unless required by applicable law or agreed to in writing, 13 | *software distributed under the License is distributed on an 14 | *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | *KIND, either express or implied. See the License for the 16 | *specific language governing permissions and limitations 17 | *under the License. 18 | */ 19 | 20 | /* 21 | * A class to model a Usergrid folder. 22 | * 23 | * @constructor 24 | * @param {object} options {name:"MyPhotos", path:"/user/uploads", owner:"00000000-0000-0000-0000-000000000000" } 25 | * @returns {callback} callback(err, folder) 26 | */ 27 | Usergrid.Folder = function(options, callback) { 28 | var self = this, 29 | messages = []; 30 | console.log("FOLDER OPTIONS", options); 31 | self._client = options.client; 32 | self._data = options.data || {}; 33 | self._data.type = "folders"; 34 | var missingData = ["name", "owner", "path"].some(function(required) { 35 | return !(required in self._data); 36 | }); 37 | if (missingData) { 38 | return doCallback(callback, [new UsergridInvalidArgumentError("Invalid asset data: 'name', 'owner', and 'path' are required properties."), null, self], self); 39 | } 40 | self.save(function(err, response) { 41 | if (err) { 42 | doCallback(callback, [new UsergridError(response), response, self], self); 43 | } else { 44 | if (response && response.entities && response.entities.length) { 45 | self.set(response.entities[0]); 46 | } 47 | doCallback(callback, [null, response, self], self); 48 | } 49 | }); 50 | }; 51 | /* 52 | * Inherit from Usergrid.Entity. 53 | */ 54 | Usergrid.Folder.prototype = new Usergrid.Entity(); 55 | 56 | 57 | /* 58 | * fetch the folder and associated assets 59 | * 60 | * @method fetch 61 | * @public 62 | * @param {function} callback(err, self) 63 | * @returns {callback} callback(err, self) 64 | */ 65 | Usergrid.Folder.prototype.fetch = function(callback) { 66 | var self = this; 67 | Usergrid.Entity.prototype.fetch.call(self, function(err, data) { 68 | console.log("self", self.get()); 69 | console.log("data", data); 70 | if (!err) { 71 | self.getAssets(function(err, response) { 72 | if (err) { 73 | doCallback(callback, [new UsergridError(response), resonse, self], self); 74 | } else { 75 | doCallback(callback, [null, self], self); 76 | } 77 | }); 78 | } else { 79 | doCallback(callback, [null, data, self], self); 80 | } 81 | }); 82 | }; 83 | /* 84 | * Add an asset to the folder. 85 | * 86 | * @method addAsset 87 | * @public 88 | * @param {object} options {asset:(uuid || Usergrid.Asset || {name:"photo.jpg", path:"/user/uploads", "content-type":"image/jpeg", owner:"F01DE600-0000-0000-0000-000000000000" }) } 89 | * @returns {callback} callback(err, folder) 90 | */ 91 | Usergrid.Folder.prototype.addAsset = function(options, callback) { 92 | var self = this; 93 | if (('asset' in options)) { 94 | var asset = null; 95 | switch (typeof options.asset) { 96 | case 'object': 97 | asset = options.asset; 98 | if (!(asset instanceof Usergrid.Entity)) { 99 | asset = new Usergrid.Asset(asset); 100 | } 101 | break; 102 | case 'string': 103 | if (isUUID(options.asset)) { 104 | asset = new Usergrid.Asset({ 105 | client: self._client, 106 | data: { 107 | uuid: options.asset, 108 | type: "assets" 109 | } 110 | }); 111 | } 112 | break; 113 | } 114 | if (asset && asset instanceof Usergrid.Entity) { 115 | asset.fetch(function(err, data) { 116 | if (err) { 117 | doCallback(callback, [new UsergridError(data), data, self], self); 118 | } else { 119 | var endpoint = ["folders", self.get("uuid"), "assets", asset.get("uuid")].join('/'); 120 | var options = { 121 | method: 'POST', 122 | endpoint: endpoint 123 | }; 124 | self._client.request(options, callback); 125 | } 126 | }); 127 | } 128 | } else { 129 | //nothing to add 130 | doCallback(callback, [new UsergridInvalidArgumentError("No asset specified"), null, self], self); 131 | } 132 | }; 133 | 134 | /* 135 | * Remove an asset from the folder. 136 | * 137 | * @method removeAsset 138 | * @public 139 | * @param {object} options {asset:(uuid || Usergrid.Asset || {name:"photo.jpg", path:"/user/uploads", "content-type":"image/jpeg", owner:"F01DE600-0000-0000-0000-000000000000" }) } 140 | * @returns {callback} callback(err, folder) 141 | */ 142 | Usergrid.Folder.prototype.removeAsset = function(options, callback) { 143 | var self = this; 144 | if (('asset' in options)) { 145 | var asset = null; 146 | switch (typeof options.asset) { 147 | case 'object': 148 | asset = options.asset; 149 | break; 150 | case 'string': 151 | if (isUUID(options.asset)) { 152 | asset = new Usergrid.Asset({ 153 | client: self._client, 154 | data: { 155 | uuid: options.asset, 156 | type: "assets" 157 | } 158 | }); 159 | } 160 | break; 161 | } 162 | if (asset && asset !== null) { 163 | var endpoint = ["folders", self.get("uuid"), "assets", asset.get("uuid")].join('/'); 164 | self._client.request({ 165 | method: 'DELETE', 166 | endpoint: endpoint 167 | }, function(err, response) { 168 | if (err) { 169 | doCallback(callback, [new UsergridError(response), response, self], self); 170 | } else { 171 | doCallback(callback, [null, response, self], self); 172 | } 173 | }); 174 | } 175 | } else { 176 | //nothing to add 177 | doCallback(callback, [new UsergridInvalidArgumentError("No asset specified"), null, self], self); 178 | } 179 | }; 180 | 181 | /* 182 | * List the assets in the folder. 183 | * 184 | * @method getAssets 185 | * @public 186 | * @returns {callback} callback(err, assets) 187 | */ 188 | Usergrid.Folder.prototype.getAssets = function(callback) { 189 | return this.getConnections("assets", callback); 190 | }; 191 | -------------------------------------------------------------------------------- /lib/modules/Group.js: -------------------------------------------------------------------------------- 1 | /* 2 | *Licensed to the Apache Software Foundation (ASF) under one 3 | *or more contributor license agreements. See the NOTICE file 4 | *distributed with this work for additional information 5 | *regarding copyright ownership. The ASF licenses this file 6 | *to you under the Apache License, Version 2.0 (the 7 | *"License"); you may not use this file except in compliance 8 | *with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | *Unless required by applicable law or agreed to in writing, 13 | *software distributed under the License is distributed on an 14 | *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | *KIND, either express or implied. See the License for the 16 | *specific language governing permissions and limitations 17 | *under the License. 18 | */ 19 | 20 | 21 | /* 22 | * A class to model a Usergrid group. 23 | * Set the path in the options object. 24 | * 25 | * @constructor 26 | * @param {object} options {client:client, data: {'key': 'value'}, path:'path'} 27 | */ 28 | Usergrid.Group = function(options, callback) { 29 | this._path = options.path; 30 | this._list = []; 31 | this._client = options.client; 32 | this._data = options.data || {}; 33 | this._data.type = "groups"; 34 | }; 35 | 36 | /* 37 | * Inherit from Usergrid.Entity. 38 | * Note: This only accounts for data on the group object itself. 39 | * You need to use add and remove to manipulate group membership. 40 | */ 41 | Usergrid.Group.prototype = new Usergrid.Entity(); 42 | 43 | /* 44 | * Fetches current group data, and members. 45 | * 46 | * @method fetch 47 | * @public 48 | * @param {function} callback 49 | * @returns {function} callback(err, data) 50 | */ 51 | Usergrid.Group.prototype.fetch = function(callback) { 52 | var self = this; 53 | var groupEndpoint = 'groups/' + this._path; 54 | var memberEndpoint = 'groups/' + this._path + '/users'; 55 | 56 | var groupOptions = { 57 | method: 'GET', 58 | endpoint: groupEndpoint 59 | }; 60 | 61 | var memberOptions = { 62 | method: 'GET', 63 | endpoint: memberEndpoint 64 | }; 65 | 66 | this._client.request(groupOptions, function(err, response) { 67 | if (err) { 68 | if (self._client.logging) { 69 | console.log('error getting group'); 70 | } 71 | doCallback(callback, [err, response], self); 72 | } else { 73 | var entities = response.getEntities(); 74 | if (entities && entities.length) { 75 | var groupresponse = entities.shift(); 76 | //self._response = groupresponse || {}; 77 | self._client.request(memberOptions, function(err, response) { 78 | if (err && self._client.logging) { 79 | console.log('error getting group users'); 80 | } else { 81 | self._list = response.getEntities() 82 | .filter(function(entity) { 83 | return isUUID(entity.uuid); 84 | }) 85 | .map(function(entity) { 86 | return new Usergrid.Entity({ 87 | type: entity.type, 88 | client: self._client, 89 | uuid: entity.uuid, 90 | response: entity //TODO: deprecate this property 91 | }); 92 | }); 93 | } 94 | doCallback(callback, [err, response, self], self); 95 | }); 96 | } 97 | } 98 | }); 99 | }; 100 | 101 | /* 102 | * Retrieves the members of a group. 103 | * 104 | * @method members 105 | * @public 106 | * @param {function} callback 107 | * @return {function} callback(err, data); 108 | */ 109 | Usergrid.Group.prototype.members = function(callback) { 110 | //doCallback(callback, [null, this._list, this], this); 111 | return this._list; 112 | }; 113 | 114 | /* 115 | * Adds an existing user to the group, and refreshes the group object. 116 | * 117 | * Options object: {user: user_entity} 118 | * 119 | * @method add 120 | * @public 121 | * @params {object} options 122 | * @param {function} callback 123 | * @return {function} callback(err, data) 124 | */ 125 | Usergrid.Group.prototype.add = function(options, callback) { 126 | var self = this; 127 | if (options.user) { 128 | options = { 129 | method: "POST", 130 | endpoint: "groups/" + this._path + "/users/" + options.user.get('username') 131 | }; 132 | this._client.request(options, function(error, response) { 133 | if (error) { 134 | doCallback(callback, [error, response, self], self); 135 | } else { 136 | self.fetch(callback); 137 | } 138 | }); 139 | } else { 140 | doCallback(callback, [new UsergridError("no user specified", 'no_user_specified'), null, this], this); 141 | } 142 | }; 143 | 144 | /* 145 | * Removes a user from a group, and refreshes the group object. 146 | * 147 | * Options object: {user: user_entity} 148 | * 149 | * @method remove 150 | * @public 151 | * @params {object} options 152 | * @param {function} callback 153 | * @return {function} callback(err, data) 154 | */ 155 | Usergrid.Group.prototype.remove = function(options, callback) { 156 | var self = this; 157 | if (options.user) { 158 | options = { 159 | method: "DELETE", 160 | endpoint: "groups/" + this._path + "/users/" + options.user.username 161 | }; 162 | this._client.request(options, function(error, response) { 163 | if (error) { 164 | doCallback(callback, [error, response, self], self); 165 | } else { 166 | self.fetch(callback); 167 | } 168 | }); 169 | } else { 170 | doCallback(callback, [new UsergridError("no user specified", 'no_user_specified'), null, this], this); 171 | } 172 | }; 173 | 174 | /* 175 | * Gets feed for a group. 176 | * 177 | * @public 178 | * @method feed 179 | * @param {function} callback 180 | * @returns {callback} callback(err, data, activities) 181 | */ 182 | Usergrid.Group.prototype.feed = function(callback) { 183 | var self = this; 184 | var options = { 185 | method: "GET", 186 | endpoint: "groups/" + this._path + "/feed" 187 | }; 188 | this._client.request(options, function(err, response) { 189 | doCallback(callback, [err, response, self], self); 190 | }); 191 | }; 192 | 193 | /* 194 | * Creates activity and posts to group feed. 195 | * 196 | * options object: {user: user_entity, content: "activity content"} 197 | * 198 | * @public 199 | * @method createGroupActivity 200 | * @params {object} options 201 | * @param {function} callback 202 | * @returns {callback} callback(err, entity) 203 | */ 204 | Usergrid.Group.prototype.createGroupActivity = function(options, callback) { 205 | var self = this; 206 | var user = options.user; 207 | var entity = new Usergrid.Entity({ 208 | client: this._client, 209 | data: { 210 | actor: { 211 | "displayName": user.get("username"), 212 | "uuid": user.get("uuid"), 213 | "username": user.get("username"), 214 | "email": user.get("email"), 215 | "picture": user.get("picture"), 216 | "image": { 217 | "duration": 0, 218 | "height": 80, 219 | "url": user.get("picture"), 220 | "width": 80 221 | }, 222 | }, 223 | "verb": "post", 224 | "content": options.content, 225 | "type": 'groups/' + this._path + '/activities' 226 | } 227 | }); 228 | entity.save(function(err, response, entity) { 229 | doCallback(callback, [err, response, self]); 230 | }); 231 | }; 232 | -------------------------------------------------------------------------------- /lib/modules/util/Ajax.js: -------------------------------------------------------------------------------- 1 | /* 2 | *Licensed to the Apache Software Foundation (ASF) under one 3 | *or more contributor license agreements. See the NOTICE file 4 | *distributed with this work for additional information 5 | *regarding copyright ownership. The ASF licenses this file 6 | *to you under the Apache License, Version 2.0 (the 7 | *"License"); you may not use this file except in compliance 8 | *with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | *Unless required by applicable law or agreed to in writing, 13 | *software distributed under the License is distributed on an 14 | *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | *KIND, either express or implied. See the License for the 16 | *specific language governing permissions and limitations 17 | *under the License. 18 | * 19 | * @author ryan bridges (rbridges@apigee.com) 20 | */ 21 | 22 | //Ajax 23 | (function() { 24 | var name = 'Ajax', global = this, overwrittenName = global[name], exports; 25 | 26 | function partial(){ 27 | var args = Array.prototype.slice.call(arguments); 28 | var fn=args.shift(); 29 | return fn.bind(this, args); 30 | } 31 | function Ajax() { 32 | this.logger=new global.Logger(name); 33 | var self=this; 34 | function encode(data) { 35 | var result = ""; 36 | if (typeof data === "string") { 37 | result = data; 38 | } else { 39 | var e = encodeURIComponent; 40 | for (var i in data) { 41 | if (data.hasOwnProperty(i)) { 42 | result += '&' + e(i) + '=' + e(data[i]); 43 | } 44 | } 45 | } 46 | return result; 47 | } 48 | function request(m, u, d) { 49 | var p = new Promise(), timeout; 50 | self.logger.time(m + ' ' + u); 51 | (function(xhr) { 52 | xhr.onreadystatechange = function() { 53 | if(this.readyState === 4){ 54 | self.logger.timeEnd(m + ' ' + u); 55 | clearTimeout(timeout); 56 | p.done(null, this); 57 | } 58 | }; 59 | xhr.onerror=function(response){ 60 | clearTimeout(timeout); 61 | p.done(response, null); 62 | }; 63 | xhr.oncomplete=function(response){ 64 | clearTimeout(timeout); 65 | self.logger.timeEnd(m + ' ' + u); 66 | self.info("%s request to %s returned %s", m, u, this.status ); 67 | }; 68 | xhr.open(m, u); 69 | if (d) { 70 | if("object"===typeof d){ 71 | d=JSON.stringify(d); 72 | } 73 | xhr.setRequestHeader("Content-Type", "application/json"); 74 | xhr.setRequestHeader("Accept", "application/json"); 75 | } 76 | timeout = setTimeout(function() { 77 | xhr.abort(); 78 | p.done("API Call timed out.", null); 79 | }, 30000); 80 | //TODO stick that timeout in a config variable 81 | xhr.send(encode(d)); 82 | }(new XMLHttpRequest())); 83 | return p; 84 | } 85 | this.request=request; 86 | this.get = partial(request,'GET'); 87 | this.post = partial(request,'POST'); 88 | this.put = partial(request,'PUT'); 89 | this.delete = partial(request,'DELETE'); 90 | } 91 | global[name] = new Ajax(); 92 | global[name].noConflict = function() { 93 | if(overwrittenName){ 94 | global[name] = overwrittenName; 95 | } 96 | return exports; 97 | }; 98 | return global[name]; 99 | }()); 100 | -------------------------------------------------------------------------------- /lib/modules/util/Event.js: -------------------------------------------------------------------------------- 1 | var UsergridEventable = function(){ 2 | throw Error("'UsergridEventable' is not intended to be invoked directly"); 3 | }; 4 | UsergridEventable.prototype = { 5 | bind : function(event, fn){ 6 | this._events = this._events || {}; 7 | this._events[event] = this._events[event] || []; 8 | this._events[event].push(fn); 9 | }, 10 | unbind : function(event, fn){ 11 | this._events = this._events || {}; 12 | if( event in this._events === false ) return; 13 | this._events[event].splice(this._events[event].indexOf(fn), 1); 14 | }, 15 | trigger : function(event /* , args... */){ 16 | this._events = this._events || {}; 17 | if( event in this._events === false ) return; 18 | for(var i = 0; i < this._events[event].length; i++){ 19 | this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)); 20 | } 21 | } 22 | }; 23 | UsergridEventable.mixin = function(destObject){ 24 | var props = ['bind', 'unbind', 'trigger']; 25 | for(var i = 0; i < props.length; i ++){ 26 | if(props[i] in destObject.prototype){ 27 | console.warn("overwriting '"+props[i]+"' on '"+destObject.name+"'."); 28 | console.warn("the previous version can be found at '_"+props[i]+"' on '"+destObject.name+"'."); 29 | destObject.prototype['_'+props[i]]=destObject.prototype[props[i]]; 30 | } 31 | destObject.prototype[props[i]] = UsergridEventable.prototype[props[i]]; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /lib/modules/util/Logger.js: -------------------------------------------------------------------------------- 1 | /* 2 | *Licensed to the Apache Software Foundation (ASF) under one 3 | *or more contributor license agreements. See the NOTICE file 4 | *distributed with this work for additional information 5 | *regarding copyright ownership. The ASF licenses this file 6 | *to you under the Apache License, Version 2.0 (the 7 | *"License"); you may not use this file except in compliance 8 | *with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | *Unless required by applicable law or agreed to in writing, 13 | *software distributed under the License is distributed on an 14 | *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | *KIND, either express or implied. See the License for the 16 | *specific language governing permissions and limitations 17 | *under the License. 18 | * 19 | * @author ryan bridges (rbridges@apigee.com) 20 | */ 21 | 22 | //Logger 23 | (function() { 24 | var name = 'Logger', global = this, overwrittenName = global[name], exports; 25 | /* logging */ 26 | function Logger(name) { 27 | this.logEnabled = true; 28 | this.init(name, true); 29 | } 30 | Logger.METHODS=[ 31 | "log", "error", "warn", "info", "debug", "assert", "clear", "count", 32 | "dir", "dirxml", "exception", "group", "groupCollapsed", "groupEnd", 33 | "profile", "profileEnd", "table", "time", "timeEnd", "trace" 34 | ]; 35 | Logger.prototype.init=function(name, logEnabled){ 36 | this.name=name||"UNKNOWN"; 37 | this.logEnabled=logEnabled||true; 38 | var addMethod=function(method){this[method]=this.createLogMethod(method);}.bind(this); 39 | Logger.METHODS.forEach(addMethod); 40 | }; 41 | Logger.prototype.createLogMethod=function(method){ 42 | return Logger.prototype.log.bind(this, method); 43 | }; 44 | Logger.prototype.prefix=function(method, args){ 45 | var prepend='['+method.toUpperCase()+']['+name+"]:\t"; 46 | if(['log', 'error', 'warn', 'info'].indexOf(method)!==-1){ 47 | if("string"===typeof args[0]){ 48 | args[0]=prepend+args[0]; 49 | }else{ 50 | args.unshift(prepend); 51 | } 52 | } 53 | return args; 54 | }; 55 | Logger.prototype.log=function(){ 56 | var args=[].slice.call(arguments); 57 | method=args.shift(); 58 | if(Logger.METHODS.indexOf(method)===-1){ 59 | method="log"; 60 | } 61 | if(!(this.logEnabled && console && console[method]))return; 62 | args=this.prefix(method, args); 63 | console[method].apply(console, args); 64 | }; 65 | Logger.prototype.setLogEnabled=function(logEnabled){ 66 | this.logEnabled=logEnabled||true; 67 | }; 68 | 69 | Logger.mixin = function(destObject){ 70 | destObject.__logger=new Logger(destObject.name||"UNKNOWN"); 71 | var addMethod=function(method){ 72 | if(method in destObject.prototype){ 73 | console.warn("overwriting '"+method+"' on '"+destObject.name+"'."); 74 | console.warn("the previous version can be found at '_"+method+"' on '"+destObject.name+"'."); 75 | destObject.prototype['_'+method]=destObject.prototype[method]; 76 | } 77 | destObject.prototype[method]=destObject.__logger.createLogMethod(method); 78 | }; 79 | Logger.METHODS.forEach(addMethod); 80 | }; 81 | global[name] = Logger; 82 | global[name].noConflict = function() { 83 | if(overwrittenName){ 84 | global[name] = overwrittenName; 85 | } 86 | return Logger; 87 | }; 88 | return global[name]; 89 | }()); 90 | -------------------------------------------------------------------------------- /lib/modules/util/Promise.js: -------------------------------------------------------------------------------- 1 | /* 2 | *Licensed to the Apache Software Foundation (ASF) under one 3 | *or more contributor license agreements. See the NOTICE file 4 | *distributed with this work for additional information 5 | *regarding copyright ownership. The ASF licenses this file 6 | *to you under the Apache License, Version 2.0 (the 7 | *"License"); you may not use this file except in compliance 8 | *with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | *Unless required by applicable law or agreed to in writing, 13 | *software distributed under the License is distributed on an 14 | *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | *KIND, either express or implied. See the License for the 16 | *specific language governing permissions and limitations 17 | *under the License. 18 | * 19 | * @author ryan bridges (rbridges@apigee.com) 20 | */ 21 | 22 | //Promise 23 | (function(global) { 24 | var name = 'Promise', overwrittenName = global[name], exports; 25 | 26 | function Promise() { 27 | this.complete = false; 28 | this.error = null; 29 | this.result = null; 30 | this.callbacks = []; 31 | } 32 | Promise.prototype.then = function(callback, context) { 33 | var f = function() { 34 | return callback.apply(context, arguments); 35 | }; 36 | if (this.complete) { 37 | f(this.error, this.result); 38 | } else { 39 | this.callbacks.push(f); 40 | } 41 | }; 42 | Promise.prototype.done = function(error, result) { 43 | this.complete = true; 44 | this.error = error; 45 | this.result = result; 46 | if(this.callbacks){ 47 | for (var i = 0; i < this.callbacks.length; i++) this.callbacks[i](error, result); 48 | this.callbacks.length = 0; 49 | } 50 | }; 51 | Promise.join = function(promises) { 52 | var p = new Promise(), 53 | total = promises.length, 54 | completed = 0, 55 | errors = [], 56 | results = []; 57 | 58 | function notifier(i) { 59 | return function(error, result) { 60 | completed += 1; 61 | errors[i] = error; 62 | results[i] = result; 63 | if (completed === total) { 64 | p.done(errors, results); 65 | } 66 | }; 67 | } 68 | for (var i = 0; i < total; i++) { 69 | promises[i]().then(notifier(i)); 70 | } 71 | return p; 72 | }; 73 | Promise.chain = function(promises, error, result) { 74 | var p = new Promise(); 75 | if (promises===null||promises.length === 0) { 76 | p.done(error, result); 77 | } else { 78 | promises[0](error, result).then(function(res, err) { 79 | promises.splice(0, 1); 80 | //self.logger.info(promises.length) 81 | if(promises){ 82 | Promise.chain(promises, res, err).then(function(r, e) { 83 | p.done(r, e); 84 | }); 85 | }else{ 86 | p.done(res, err); 87 | } 88 | }); 89 | } 90 | return p; 91 | }; 92 | 93 | global[name] = Promise; 94 | global[name].noConflict = function() { 95 | if(overwrittenName){ 96 | global[name] = overwrittenName; 97 | } 98 | return Promise; 99 | }; 100 | return global[name]; 101 | }(this)); 102 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "usergrid", 3 | "version": "0.11.0", 4 | "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.", 5 | "main": "usergrid.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "Apache 2.0", 14 | "devDependencies": { 15 | "grunt": "~0.4.2", 16 | "grunt-contrib-clean": "~0.5.0", 17 | "grunt-contrib-watch": "~0.5.3", 18 | "grunt-contrib-uglify": "~0.2.7", 19 | "grunt-blanket-mocha": "~0.3.3", 20 | "grunt-contrib-connect": "~0.6.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/mocha/index.html: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | 22 | Mocha Tests 23 | 24 | 25 | 26 | 27 | 28 | 33 | 34 | 35 | 36 | 37 | 49 | 50 | 55 | 56 | 57 | 58 |
    59 | 60 | 61 | -------------------------------------------------------------------------------- /tests/qunit/apigee_test.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | QUnit Example 23 | 24 | 25 | 26 |
    27 |
    28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/qunit/tests.js: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed to the Apache Software Foundation (ASF) under one or more 3 | // contributor license agreements. See the NOTICE file distributed with 4 | // this work for additional information regarding copyright ownership. 5 | // The ASF licenses this file to You under the Apache License, Version 2.0 6 | // (the "License"); you may not use this file except in compliance with 7 | // the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | 18 | test( "hello test", function() { 19 | ok( 1 == "1", "Passed!" ); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/resources/css/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, 13 | #mocha li { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | #mocha ul { 19 | list-style: none; 20 | } 21 | 22 | #mocha h1, 23 | #mocha h2 { 24 | margin: 0; 25 | } 26 | 27 | #mocha h1 { 28 | margin-top: 15px; 29 | font-size: 1em; 30 | font-weight: 200; 31 | } 32 | 33 | #mocha h1 a { 34 | text-decoration: none; 35 | color: inherit; 36 | } 37 | 38 | #mocha h1 a:hover { 39 | text-decoration: underline; 40 | } 41 | 42 | #mocha .suite .suite h1 { 43 | margin-top: 0; 44 | font-size: .8em; 45 | } 46 | 47 | #mocha .hidden { 48 | display: none; 49 | } 50 | 51 | #mocha h2 { 52 | font-size: 12px; 53 | font-weight: normal; 54 | cursor: pointer; 55 | } 56 | 57 | #mocha .suite { 58 | margin-left: 15px; 59 | } 60 | 61 | #mocha .test { 62 | margin-left: 15px; 63 | overflow: hidden; 64 | } 65 | 66 | #mocha .test.pending:hover h2::after { 67 | content: '(pending)'; 68 | font-family: arial, sans-serif; 69 | } 70 | 71 | #mocha .test.pass.medium .duration { 72 | background: #c09853; 73 | } 74 | 75 | #mocha .test.pass.slow .duration { 76 | background: #b94a48; 77 | } 78 | 79 | #mocha .test.pass::before { 80 | content: '✓'; 81 | font-size: 12px; 82 | display: block; 83 | float: left; 84 | margin-right: 5px; 85 | color: #00d6b2; 86 | } 87 | 88 | #mocha .test.pass .duration { 89 | font-size: 9px; 90 | margin-left: 5px; 91 | padding: 2px 5px; 92 | color: #fff; 93 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 95 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 96 | -webkit-border-radius: 5px; 97 | -moz-border-radius: 5px; 98 | -ms-border-radius: 5px; 99 | -o-border-radius: 5px; 100 | border-radius: 5px; 101 | } 102 | 103 | #mocha .test.pass.fast .duration { 104 | display: none; 105 | } 106 | 107 | #mocha .test.pending { 108 | color: #0b97c4; 109 | } 110 | 111 | #mocha .test.pending::before { 112 | content: '◦'; 113 | color: #0b97c4; 114 | } 115 | 116 | #mocha .test.fail { 117 | color: #c00; 118 | } 119 | 120 | #mocha .test.fail pre { 121 | color: black; 122 | } 123 | 124 | #mocha .test.fail::before { 125 | content: '✖'; 126 | font-size: 12px; 127 | display: block; 128 | float: left; 129 | margin-right: 5px; 130 | color: #c00; 131 | } 132 | 133 | #mocha .test pre.error { 134 | color: #c00; 135 | max-height: 300px; 136 | overflow: auto; 137 | } 138 | 139 | /** 140 | * (1): approximate for browsers not supporting calc 141 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) 142 | * ^^ seriously 143 | */ 144 | #mocha .test pre { 145 | display: block; 146 | float: left; 147 | clear: left; 148 | font: 12px/1.5 monaco, monospace; 149 | margin: 5px; 150 | padding: 15px; 151 | border: 1px solid #eee; 152 | max-width: 85%; /*(1)*/ 153 | max-width: calc(100% - 42px); /*(2)*/ 154 | word-wrap: break-word; 155 | border-bottom-color: #ddd; 156 | -webkit-border-radius: 3px; 157 | -webkit-box-shadow: 0 1px 3px #eee; 158 | -moz-border-radius: 3px; 159 | -moz-box-shadow: 0 1px 3px #eee; 160 | border-radius: 3px; 161 | } 162 | 163 | #mocha .test h2 { 164 | position: relative; 165 | } 166 | 167 | #mocha .test a.replay { 168 | position: absolute; 169 | top: 3px; 170 | right: 0; 171 | text-decoration: none; 172 | vertical-align: middle; 173 | display: block; 174 | width: 15px; 175 | height: 15px; 176 | line-height: 15px; 177 | text-align: center; 178 | background: #eee; 179 | font-size: 15px; 180 | -moz-border-radius: 15px; 181 | border-radius: 15px; 182 | -webkit-transition: opacity 200ms; 183 | -moz-transition: opacity 200ms; 184 | transition: opacity 200ms; 185 | opacity: 0.3; 186 | color: #888; 187 | } 188 | 189 | #mocha .test:hover a.replay { 190 | opacity: 1; 191 | } 192 | 193 | #mocha-report.pass .test.fail { 194 | display: none; 195 | } 196 | 197 | #mocha-report.fail .test.pass { 198 | display: none; 199 | } 200 | 201 | #mocha-report.pending .test.pass, 202 | #mocha-report.pending .test.fail { 203 | display: none; 204 | } 205 | #mocha-report.pending .test.pass.pending { 206 | display: block; 207 | } 208 | 209 | #mocha-error { 210 | color: #c00; 211 | font-size: 1.5em; 212 | font-weight: 100; 213 | letter-spacing: 1px; 214 | } 215 | 216 | #mocha-stats { 217 | position: fixed; 218 | top: 15px; 219 | right: 10px; 220 | font-size: 12px; 221 | margin: 0; 222 | color: #888; 223 | z-index: 1; 224 | } 225 | 226 | #mocha-stats .progress { 227 | float: right; 228 | padding-top: 0; 229 | } 230 | 231 | #mocha-stats em { 232 | color: black; 233 | } 234 | 235 | #mocha-stats a { 236 | text-decoration: none; 237 | color: inherit; 238 | } 239 | 240 | #mocha-stats a:hover { 241 | border-bottom: 1px solid #eee; 242 | } 243 | 244 | #mocha-stats li { 245 | display: inline-block; 246 | margin: 0 5px; 247 | list-style: none; 248 | padding-top: 11px; 249 | } 250 | 251 | #mocha-stats canvas { 252 | width: 40px; 253 | height: 40px; 254 | } 255 | 256 | #mocha code .comment { color: #ddd; } 257 | #mocha code .init { color: #2f6fad; } 258 | #mocha code .string { color: #5890ad; } 259 | #mocha code .keyword { color: #8a6343; } 260 | #mocha code .number { color: #2f6fad; } 261 | 262 | @media screen and (max-device-width: 480px) { 263 | #mocha { 264 | margin: 60px 0px; 265 | } 266 | 267 | #mocha #stats { 268 | position: absolute; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /tests/resources/css/styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * All Calls is a Node.js sample app that is powered by Usergrid 3 | * This app shows how to make the 4 REST calls (GET, POST, 4 | * PUT, DELETE) against the usergrid API. 5 | * 6 | * Learn more at http://Usergrid.com/docs 7 | * 8 | * Copyright 2012 Apigee Corporation 9 | * 10 | * Licensed under the Apache License, Version 2.0 (the "License"); 11 | * you may not use this file except in compliance with the License. 12 | * You may obtain a copy of the License at 13 | * 14 | * http://www.apache.org/licenses/LICENSE-2.0 15 | * 16 | * Unless required by applicable law or agreed to in writing, software 17 | * distributed under the License is distributed on an "AS IS" BASIS, 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | * See the License for the specific language governing permissions and 20 | * limitations under the License. 21 | */ 22 | 23 | /** 24 | * @file styles.css 25 | * @author Rod Simpson (rod@apigee.com) 26 | * 27 | */ 28 | 29 | body { 30 | background-color: #fff; 31 | min-height: 800px; 32 | } 33 | 34 | /* buttons ================================================================= */ 35 | .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;} 36 | 37 | .header{ 38 | padding: 10px; 39 | width: 100%; 40 | height: 40px; 41 | background-color: #ff4200; 42 | color: #fff; 43 | text-align: left; 44 | font-size: 16px; 45 | font-weight: 800; 46 | } 47 | .breadcrumb{ 48 | font-size: 16px; 49 | } 50 | .info{ 51 | padding: 0px 30px 30px 30px; 52 | font-size: 16px; 53 | } 54 | h3{ 55 | padding-bottom: 20px; 56 | } 57 | .main{ 58 | display: block; 59 | padding: 0 30px 30px 30px ; 60 | background-color: #fff; 61 | } 62 | .form-block{ 63 | display: block; 64 | display: none; 65 | padding: 10px 0; 66 | min-height: 210px; 67 | background-color: #fff; 68 | } 69 | .section-header{ 70 | font-size: 20px; 71 | font-weight: 200; 72 | padding-bottom: 20px; 73 | } 74 | .note { 75 | padding-bottom: 20px; 76 | } 77 | .response-box{ 78 | margin: 0 auto; 79 | padding: 10px; 80 | width: 640px; 81 | border: 1px solid silver; 82 | background-color: #ddd; 83 | font-weight: bold; 84 | } 85 | pre{ 86 | border: none; 87 | padding: 0; 88 | } 89 | .left{ 90 | float: left; 91 | } -------------------------------------------------------------------------------- /tests/resources/images/apigee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apache/usergrid-javascript/3303e561f09d013ba11cece4b024a068f2c8a8d2/tests/resources/images/apigee.png -------------------------------------------------------------------------------- /tests/resources/js/json2.js: -------------------------------------------------------------------------------- 1 | /* 2 | json2.js 3 | 2012-10-08 4 | 5 | Public Domain. 6 | 7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 | 9 | See http://www.JSON.org/js.html 10 | 11 | 12 | This code should be minified before deployment. 13 | See http://javascript.crockford.com/jsmin.html 14 | 15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 16 | NOT CONTROL. 17 | 18 | 19 | This file creates a global JSON object containing two methods: stringify 20 | and parse. 21 | 22 | JSON.stringify(value, replacer, space) 23 | value any JavaScript value, usually an object or array. 24 | 25 | replacer an optional parameter that determines how object 26 | values are stringified for objects. It can be a 27 | function or an array of strings. 28 | 29 | space an optional parameter that specifies the indentation 30 | of nested structures. If it is omitted, the text will 31 | be packed without extra whitespace. If it is a number, 32 | it will specify the number of spaces to indent at each 33 | level. If it is a string (such as '\t' or ' '), 34 | it contains the characters used to indent at each level. 35 | 36 | This method produces a JSON text from a JavaScript value. 37 | 38 | When an object value is found, if the object contains a toJSON 39 | method, its toJSON method will be called and the result will be 40 | stringified. A toJSON method does not serialize: it returns the 41 | value represented by the name/value pair that should be serialized, 42 | or undefined if nothing should be serialized. The toJSON method 43 | will be passed the key associated with the value, and this will be 44 | bound to the value 45 | 46 | For example, this would serialize Dates as ISO strings. 47 | 48 | Date.prototype.toJSON = function (key) { 49 | function f(n) { 50 | // Format integers to have at least two digits. 51 | return n < 10 ? '0' + n : n; 52 | } 53 | 54 | return this.getUTCFullYear() + '-' + 55 | f(this.getUTCMonth() + 1) + '-' + 56 | f(this.getUTCDate()) + 'T' + 57 | f(this.getUTCHours()) + ':' + 58 | f(this.getUTCMinutes()) + ':' + 59 | f(this.getUTCSeconds()) + 'Z'; 60 | }; 61 | 62 | You can provide an optional replacer method. It will be passed the 63 | key and value of each member, with this bound to the containing 64 | object. The value that is returned from your method will be 65 | serialized. If your method returns undefined, then the member will 66 | be excluded from the serialization. 67 | 68 | If the replacer parameter is an array of strings, then it will be 69 | used to select the members to be serialized. It filters the results 70 | such that only members with keys listed in the replacer array are 71 | stringified. 72 | 73 | Values that do not have JSON representations, such as undefined or 74 | functions, will not be serialized. Such values in objects will be 75 | dropped; in arrays they will be replaced with null. You can use 76 | a replacer function to replace those with JSON values. 77 | JSON.stringify(undefined) returns undefined. 78 | 79 | The optional space parameter produces a stringification of the 80 | value that is filled with line breaks and indentation to make it 81 | easier to read. 82 | 83 | If the space parameter is a non-empty string, then that string will 84 | be used for indentation. If the space parameter is a number, then 85 | the indentation will be that many spaces. 86 | 87 | Example: 88 | 89 | text = JSON.stringify(['e', {pluribus: 'unum'}]); 90 | // text is '["e",{"pluribus":"unum"}]' 91 | 92 | 93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 95 | 96 | text = JSON.stringify([new Date()], function (key, value) { 97 | return this[key] instanceof Date ? 98 | 'Date(' + this[key] + ')' : value; 99 | }); 100 | // text is '["Date(---current time---)"]' 101 | 102 | 103 | JSON.parse(text, reviver) 104 | This method parses a JSON text to produce an object or array. 105 | It can throw a SyntaxError exception. 106 | 107 | The optional reviver parameter is a function that can filter and 108 | transform the results. It receives each of the keys and values, 109 | and its return value is used instead of the original value. 110 | If it returns what it received, then the structure is not modified. 111 | If it returns undefined then the member is deleted. 112 | 113 | Example: 114 | 115 | // Parse the text. Values that look like ISO date strings will 116 | // be converted to Date objects. 117 | 118 | myData = JSON.parse(text, function (key, value) { 119 | var a; 120 | if (typeof value === 'string') { 121 | a = 122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 123 | if (a) { 124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 125 | +a[5], +a[6])); 126 | } 127 | } 128 | return value; 129 | }); 130 | 131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 132 | var d; 133 | if (typeof value === 'string' && 134 | value.slice(0, 5) === 'Date(' && 135 | value.slice(-1) === ')') { 136 | d = new Date(value.slice(5, -1)); 137 | if (d) { 138 | return d; 139 | } 140 | } 141 | return value; 142 | }); 143 | 144 | 145 | This is a reference implementation. You are free to copy, modify, or 146 | redistribute. 147 | */ 148 | 149 | /*jslint evil: true, regexp: true */ 150 | 151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 154 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 155 | test, toJSON, toString, valueOf 156 | */ 157 | 158 | 159 | // Create a JSON object only if one does not already exist. We create the 160 | // methods in a closure to avoid creating global variables. 161 | 162 | if (typeof JSON !== 'object') { 163 | JSON = {}; 164 | } 165 | 166 | (function () { 167 | 'use strict'; 168 | 169 | function f(n) { 170 | // Format integers to have at least two digits. 171 | return n < 10 ? '0' + n : n; 172 | } 173 | 174 | if (typeof Date.prototype.toJSON !== 'function') { 175 | 176 | Date.prototype.toJSON = function (key) { 177 | 178 | return isFinite(this.valueOf()) 179 | ? this.getUTCFullYear() + '-' + 180 | f(this.getUTCMonth() + 1) + '-' + 181 | f(this.getUTCDate()) + 'T' + 182 | f(this.getUTCHours()) + ':' + 183 | f(this.getUTCMinutes()) + ':' + 184 | f(this.getUTCSeconds()) + 'Z' 185 | : null; 186 | }; 187 | 188 | String.prototype.toJSON = 189 | Number.prototype.toJSON = 190 | Boolean.prototype.toJSON = function (key) { 191 | return this.valueOf(); 192 | }; 193 | } 194 | 195 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 196 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 197 | gap, 198 | indent, 199 | meta = { // table of character substitutions 200 | '\b': '\\b', 201 | '\t': '\\t', 202 | '\n': '\\n', 203 | '\f': '\\f', 204 | '\r': '\\r', 205 | '"' : '\\"', 206 | '\\': '\\\\' 207 | }, 208 | rep; 209 | 210 | 211 | function quote(string) { 212 | 213 | // If the string contains no control characters, no quote characters, and no 214 | // backslash characters, then we can safely slap some quotes around it. 215 | // Otherwise we must also replace the offending characters with safe escape 216 | // sequences. 217 | 218 | escapable.lastIndex = 0; 219 | return escapable.test(string) ? '"' + string.replace(escapable, function (a) { 220 | var c = meta[a]; 221 | return typeof c === 'string' 222 | ? c 223 | : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 224 | }) + '"' : '"' + string + '"'; 225 | } 226 | 227 | 228 | function str(key, holder) { 229 | 230 | // Produce a string from holder[key]. 231 | 232 | var i, // The loop counter. 233 | k, // The member key. 234 | v, // The member value. 235 | length, 236 | mind = gap, 237 | partial, 238 | value = holder[key]; 239 | 240 | // If the value has a toJSON method, call it to obtain a replacement value. 241 | 242 | if (value && typeof value === 'object' && 243 | typeof value.toJSON === 'function') { 244 | value = value.toJSON(key); 245 | } 246 | 247 | // If we were called with a replacer function, then call the replacer to 248 | // obtain a replacement value. 249 | 250 | if (typeof rep === 'function') { 251 | value = rep.call(holder, key, value); 252 | } 253 | 254 | // What happens next depends on the value's type. 255 | 256 | switch (typeof value) { 257 | case 'string': 258 | return quote(value); 259 | 260 | case 'number': 261 | 262 | // JSON numbers must be finite. Encode non-finite numbers as null. 263 | 264 | return isFinite(value) ? String(value) : 'null'; 265 | 266 | case 'boolean': 267 | case 'null': 268 | 269 | // If the value is a boolean or null, convert it to a string. Note: 270 | // typeof null does not produce 'null'. The case is included here in 271 | // the remote chance that this gets fixed someday. 272 | 273 | return String(value); 274 | 275 | // If the type is 'object', we might be dealing with an object or an array or 276 | // null. 277 | 278 | case 'object': 279 | 280 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 281 | // so watch out for that case. 282 | 283 | if (!value) { 284 | return 'null'; 285 | } 286 | 287 | // Make an array to hold the partial results of stringifying this object value. 288 | 289 | gap += indent; 290 | partial = []; 291 | 292 | // Is the value an array? 293 | 294 | if (Object.prototype.toString.apply(value) === '[object Array]') { 295 | 296 | // The value is an array. Stringify every element. Use null as a placeholder 297 | // for non-JSON values. 298 | 299 | length = value.length; 300 | for (i = 0; i < length; i += 1) { 301 | partial[i] = str(i, value) || 'null'; 302 | } 303 | 304 | // Join all of the elements together, separated with commas, and wrap them in 305 | // brackets. 306 | 307 | v = partial.length === 0 308 | ? '[]' 309 | : gap 310 | ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' 311 | : '[' + partial.join(',') + ']'; 312 | gap = mind; 313 | return v; 314 | } 315 | 316 | // If the replacer is an array, use it to select the members to be stringified. 317 | 318 | if (rep && typeof rep === 'object') { 319 | length = rep.length; 320 | for (i = 0; i < length; i += 1) { 321 | if (typeof rep[i] === 'string') { 322 | k = rep[i]; 323 | v = str(k, value); 324 | if (v) { 325 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 326 | } 327 | } 328 | } 329 | } else { 330 | 331 | // Otherwise, iterate through all of the keys in the object. 332 | 333 | for (k in value) { 334 | if (Object.prototype.hasOwnProperty.call(value, k)) { 335 | v = str(k, value); 336 | if (v) { 337 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 338 | } 339 | } 340 | } 341 | } 342 | 343 | // Join all of the member texts together, separated with commas, 344 | // and wrap them in braces. 345 | 346 | v = partial.length === 0 347 | ? '{}' 348 | : gap 349 | ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' 350 | : '{' + partial.join(',') + '}'; 351 | gap = mind; 352 | return v; 353 | } 354 | } 355 | 356 | // If the JSON object does not yet have a stringify method, give it one. 357 | 358 | if (typeof JSON.stringify !== 'function') { 359 | JSON.stringify = function (value, replacer, space) { 360 | 361 | // The stringify method takes a value and an optional replacer, and an optional 362 | // space parameter, and returns a JSON text. The replacer can be a function 363 | // that can replace values, or an array of strings that will select the keys. 364 | // A default replacer method can be provided. Use of the space parameter can 365 | // produce text that is more easily readable. 366 | 367 | var i; 368 | gap = ''; 369 | indent = ''; 370 | 371 | // If the space parameter is a number, make an indent string containing that 372 | // many spaces. 373 | 374 | if (typeof space === 'number') { 375 | for (i = 0; i < space; i += 1) { 376 | indent += ' '; 377 | } 378 | 379 | // If the space parameter is a string, it will be used as the indent string. 380 | 381 | } else if (typeof space === 'string') { 382 | indent = space; 383 | } 384 | 385 | // If there is a replacer, it must be a function or an array. 386 | // Otherwise, throw an error. 387 | 388 | rep = replacer; 389 | if (replacer && typeof replacer !== 'function' && 390 | (typeof replacer !== 'object' || 391 | typeof replacer.length !== 'number')) { 392 | throw new Error('JSON.stringify'); 393 | } 394 | 395 | // Make a fake root object containing our value under the key of ''. 396 | // Return the result of stringifying the value. 397 | 398 | return str('', {'': value}); 399 | }; 400 | } 401 | 402 | 403 | // If the JSON object does not yet have a parse method, give it one. 404 | 405 | if (typeof JSON.parse !== 'function') { 406 | JSON.parse = function (text, reviver) { 407 | 408 | // The parse method takes a text and an optional reviver function, and returns 409 | // a JavaScript value if the text is a valid JSON text. 410 | 411 | var j; 412 | 413 | function walk(holder, key) { 414 | 415 | // The walk method is used to recursively walk the resulting structure so 416 | // that modifications can be made. 417 | 418 | var k, v, value = holder[key]; 419 | if (value && typeof value === 'object') { 420 | for (k in value) { 421 | if (Object.prototype.hasOwnProperty.call(value, k)) { 422 | v = walk(value, k); 423 | if (v !== undefined) { 424 | value[k] = v; 425 | } else { 426 | delete value[k]; 427 | } 428 | } 429 | } 430 | } 431 | return reviver.call(holder, key, value); 432 | } 433 | 434 | 435 | // Parsing happens in four stages. In the first stage, we replace certain 436 | // Unicode characters with escape sequences. JavaScript handles many characters 437 | // incorrectly, either silently deleting them, or treating them as line endings. 438 | 439 | text = String(text); 440 | cx.lastIndex = 0; 441 | if (cx.test(text)) { 442 | text = text.replace(cx, function (a) { 443 | return '\\u' + 444 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 445 | }); 446 | } 447 | 448 | // In the second stage, we run the text against regular expressions that look 449 | // for non-JSON patterns. We are especially concerned with '()' and 'new' 450 | // because they can cause invocation, and '=' because it can cause mutation. 451 | // But just to be safe, we want to reject all unexpected forms. 452 | 453 | // We split the second stage into 4 regexp operations in order to work around 454 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 455 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 456 | // replace all simple value tokens with ']' characters. Third, we delete all 457 | // open brackets that follow a colon or comma or that begin the text. Finally, 458 | // we look to see that the remaining characters are only whitespace or ']' or 459 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 460 | 461 | if (/^[\],:{}\s]*$/ 462 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') 463 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') 464 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 465 | 466 | // In the third stage we use the eval function to compile the text into a 467 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity 468 | // in JavaScript: it can begin a block or an object literal. We wrap the text 469 | // in parens to eliminate the ambiguity. 470 | 471 | j = eval('(' + text + ')'); 472 | 473 | // In the optional fourth stage, we recursively walk the new structure, passing 474 | // each name/value pair to a reviver function for possible transformation. 475 | 476 | return typeof reviver === 'function' 477 | ? walk({'': j}, '') 478 | : j; 479 | } 480 | 481 | // If the text is not JSON parseable, then a SyntaxError is thrown. 482 | 483 | throw new SyntaxError('JSON.parse'); 484 | }; 485 | } 486 | }()); 487 | -------------------------------------------------------------------------------- /tests/test.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | Readme File Tests 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 |
    34 | App Services (Usergrid) Javascript SDK 35 |
    36 |
    37 | 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. 38 |
    39 |
    40 |
    README sample code tests
    41 |
    42 |
    43 |
    44 | 45 |   46 |
    47 |
    48 |
    49 |
    Test Output
    50 |
    51 |
    // Press Start button to begin
    52 |
    53 | 54 | 55 | --------------------------------------------------------------------------------