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 |
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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
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 |
--------------------------------------------------------------------------------
/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/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 |
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
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 |
56 |
Dogs
57 |
58 | Use this form to create a new dog.
59 |
60 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/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/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 |
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 |
68 | // Press 'Log in' to log in with Facebook.
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/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 | });
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/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/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('
');
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('
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------