PageRenderTime 7ms CodeModel.GetById 2ms app.highlight 40ms RepoModel.GetById 2ms app.codeStats 0ms

/node_modules/stackmob-nodejs/stackmob-js-0.1.0.js

https://bitbucket.org/steerapi/orbit-measure
JavaScript | 823 lines | 600 code | 166 blank | 57 comment | 172 complexity | 6ebb1a17b067157c6e5a7af63a551b48 MD5 | raw file
  1//Change to suppoer nodejs
  2_ = require("underscore");
  3Backbone = require("backbone");
  4
  5(function() {
  6  var root = this;
  7  
  8  /**
  9   * StackMob Object
 10   * This holds configuration information for StackMob
 11   */
 12   //Change to suppoer nodejs
 13  module.exports = StackMob = root.StackMob = {
 14    DEFAULT_API_VERSION: 0,
 15
 16    DEFAULT_LOGIN_SCHEMA: 'user',
 17    DEFAULT_LOGIN_FIELD: 'username',
 18    DEFAULT_PASSWORD_FIELD: 'password',
 19
 20    EARTH_RADIANS_MI: 3956.6,
 21    EARTH_RADIANS_KM: 6367.5,
 22
 23    FORCE_CREATE_REQUEST: 'stackmob_force_create_request',
 24    ARRAY_FIELDNAME: 'stackmob_array_fieldname',
 25    ARRAY_VALUES: 'stackmob_array_values',
 26    CASCADE_DELETE: 'stackmob_cascade_delete',
 27
 28    HARD_DELETE: true,
 29    SOFT_DELETE: false,
 30
 31    apiVersion: 0,
 32    sdkVersion: "0.1.0",
 33
 34    getDevAPIBase: function() { return "/"; },
 35    getProdAPIBase: function() { return "/"; },
 36    getCCAPIBase: function() { return "/"},
 37
 38    METHOD_MAP: {
 39      "create": "POST",
 40      "read": "GET",
 41      "update": "PUT",
 42      "delete": "DELETE",
 43
 44      "appendAndCreate": "POST",
 45      "appendAndSave": "PUT",
 46      "deleteAndSave": "DELETE",
 47
 48      "login": "GET",
 49      "logout": "GET",
 50
 51      "loginWithFacebookToken": "GET",
 52      "createUserWithFacebook": "GET",
 53      "linkUserWithFacebook": "GET",
 54
 55      "cc": "GET"
 56    },
 57
 58    /**
 59     * Convenience method to retrieve the value of a key in an object.  If it's a function, give its return value.
 60     */
 61    getProperty: function(object, prop) {
 62      if (!(object && object[prop])) return null;
 63      return _.isFunction(object[prop]) ? object[prop]() : object[prop];
 64    },
 65
 66    /**
 67     * Externally called by user to initialize their StackMob config.
 68     */
 69    init: function(options) {
 70      options = options || {};
 71
 72      this.initStart(options); //placeholder for any actions a developer may want to implement via _extend
 73
 74      this.userSchema = options['userSchema'] || this.DEFAULT_LOGIN_SCHEMA;
 75      this.loginField = options['loginField'] || this.DEFAULT_LOGIN_FIELD;
 76      this.passwordField = options['passwordField'] || this.DEFAULT_PASSWORD_FIELD;
 77
 78      this.apiVersion = options['apiVersion'] || this.DEFAULT_API_VERSION;
 79      this.appName = this.getProperty(options, "appName") || this.throwError("An appName must be specified");
 80      this.clientSubdomain = this.getProperty(options, "clientSubdomain");
 81
 82      if (options['dev'] && options['dev'] == true) {
 83        this.debug = true;
 84        this.urlRoot = options['urlRoot'] || this.getDevAPIBase();
 85      } else {
 86        this.debug = false;
 87        this.urlRoot = options['urlRoot'] || this.getProdAPIBase();
 88      }
 89
 90      this.initEnd(options); //placeholder for any actions a developer may want to implement via _extend
 91      
 92      //Change to suppoer nodejs
 93      jsOAuth = require("./jsOAuth-1.3.3").OAuth(options);
 94      
 95      return this;
 96    },
 97
 98    initStart: function(options) {},
 99    initEnd: function(options) {}
100
101  };
102}).call(this);
103
104
105/**
106 * StackMob JS SDK
107 * BackBone.js-based
108 * Backbone.js Version 0.5.3
109 * No OAuth - for use with StackMob's HTML5 Product
110 */
111(function(){
112  var root = this;
113
114  var $ = root.jQuery || root.Ext || root.Zepto;
115
116  function isSencha() { return root.Ext; }
117  
118  function isZepto() { return root.Zepto; }
119
120  _.extend(StackMob, {
121
122    getDevAPIBase: function() { return "/"; },
123    getProdAPIBase: function() { return "/"; },
124
125    throwError: function(msg) {
126      throw new Error(msg);
127    },
128
129    urlError: function() {
130      throw new Error('A "url" property or function must be specified');
131    },
132
133    initEnd: function(options) {
134      createStackMobModel();
135      createStackMobCollection();
136      createStackMobUserModel();
137    },
138
139    cc: function(method, params, options) {
140      this.customcode(method, params, options);
141    },
142
143    customcode: function(method, params, options) {
144      options = options || {};
145      options['data'] = options['data'] || {};
146      _.extend(options['data'], params);
147      options['url'] = this.debug ? this.getDevAPIBase() : this.getProdAPIBase();
148      this.sync.call(StackMob, method, null, options);
149    },
150
151    sync: function(method, model, options) {
152      options = options || {};
153      //Override to allow 'Model#save' to force create even if the id (primary key) is set in the model and hence !isNew() in BackBone
154      var forceCreateRequest = options[StackMob.FORCE_CREATE_REQUEST] === true
155      if (forceCreateRequest) {
156        method = 'create';
157      }
158
159      function _prepareBaseURL(model, params) {
160        //User didn't override the URL so use the one defined in the model
161        if (!params['url']) {
162          if (model) params['url'] = StackMob.getProperty(model, "url");
163        }
164
165
166
167        var notCustomCode = method != 'cc';
168        var notNewModel = (model && model.isNew && !model.isNew());
169        var notForcedCreateRequest = !forceCreateRequest;
170        var isArrayMethod = (method == 'appendAndCreate' || method == 'appendAndSave' || method == 'deleteAndSave');
171
172
173        if (_isExtraMethodVerb(method)) { //Extra Method Verb? Add it to the model url. (e.g. /user/login)
174          params['url'] += (params['url'].charAt(params['url'].length - 1) == '/' ? '' : '/') + method;
175        } else if (isArrayMethod || notCustomCode && notNewModel && notForcedCreateRequest) {  //append ID in URL if necessary
176          params['url'] += (params['url'].charAt(params['url'].length - 1) == '/' ? '' : '/') +
177            encodeURIComponent(model.get(model.getPrimaryKeyField()));
178
179          if (isArrayMethod) {
180            params['url'] += '/' + options[StackMob.ARRAY_FIELDNAME];
181          }
182
183          if (method == 'deleteAndSave') {
184            var ids = '';
185
186            if (_.isArray(options[StackMob.ARRAY_VALUES])) {
187              ids = _.map(options[StackMob.ARRAY_VALUES], function(id) { return encodeURIComponent(id); }).join(',');
188            } else {
189              ids = encodeURIComponent(options[StackMob.ARRAY_VALUES]);
190            }
191
192            params['url'] += '/' + ids
193          }
194        }
195
196      }
197
198      function _prepareHeaders(params, options) {
199        //Prepare Request Headers
200        params['headers'] = params['headers'] || {};
201
202        //Add API Version Number to Request Headers
203        _.extend(params['headers'], {
204          "Accept": 'application/vnd.stackmob+json; version=' + StackMob['apiVersion'],
205          "X-StackMob-User-Agent": "StackMob (JS; " + StackMob['sdkVersion'] + ")/" + StackMob['appName'],
206          "X-StackMob-Proxy": "stackmob-api"
207        });
208
209        params['contentType'] = params['headers']['Accept'];
210
211        if (!isNaN(options[StackMob.CASCADE_DELETE])) {
212          params['headers']['X-StackMob-CascadeDelete'] = options[StackMob.CASCADE_DELETE] == true;
213        }
214
215
216        //If this is an advanced query, check headers
217        if (options['query']) {
218          //TODO throw error if no query object given
219          var queryObj = params['query'] || throwError("No StackMobQuery object provided to the query call.");
220
221
222          //Add Range Headers
223          if (queryObj.range) {
224            params['headers']['Range'] = 'objects=' + queryObj['range']['start'] + '-' + queryObj['range']['end'];
225          }
226
227          //Add Query Parameters to Parameter Map
228          _.extend(params['data'], queryObj['params']);
229
230          //Add OrderBy Headers
231          if (queryObj['orderBy'] && queryObj['orderBy'].length > 0) {
232            var orderList = queryObj['orderBy'];
233            var order = '';
234            var size = orderList.length;
235            for(var i = 0; i < size; i++) {
236              order += orderList[i];
237              if (i + 1 < size) order += ',';
238            }
239            params['headers']["X-StackMob-OrderBy"] = order;
240          }
241        }
242      }
243
244      function _prepareRequestBody(method, params, options) {
245        options = options || {};
246        //Set json content type for POST/PUT calls
247        //If this is a model, set the data to be the JSON representation of the model
248        if (params['type'] == 'POST' || params['type'] == 'PUT') {
249          if (method == 'appendAndCreate' || method == 'appendAndSave') {
250            if (options && options[StackMob.ARRAY_VALUES]) params['data'] = JSON.stringify(options[StackMob.ARRAY_VALUES]);
251          } else if (model) {
252            var json = model.toJSON();
253            delete json['lastmoddate'];
254            delete json['createddate'];
255            params['data'] = JSON.stringify(json);
256          } else params['data'] = JSON.stringify(params.data);
257        } else if (params['type'] == "GET") {
258          if (!_.isEmpty(params['data'])) {
259            params['url'] += '?';
260            var keys = _.keys(params['data']);
261
262            for (var i = 0; i < keys.length; i++) {
263              var key = keys[i]
264              var value = params['data'][key];
265              params['url'] += key + '=' + value;
266              if (i + 1 < keys.length) params['url'] += '&';
267            }
268          }
269          delete params['data']; //we shouldn't be passing anything up as data in a GET call
270        } else {
271          delete params['data'];
272        }
273      }
274
275      function _prepareAjaxClientParams(params) {
276        params = params || {};
277        //Prepare 3rd party ajax settings
278        params['processData'] = false;
279        //Put Accept into the header for jQuery
280        params['accepts'] = params['headers']["Accept"];
281      }
282
283
284      function _isExtraMethodVerb(method) {
285        return method != "create" && method != "update"
286            && method != "delete" && method != "read" && method != "query" &&
287            method != "deleteAndSave" && method != "appendAndSave" && method != "appendAndCreate";
288      }
289
290      function _makeOAuthAjaxCall(model, params) {
291        var success = params['success'];
292
293        var defaultSuccess = function(response) {
294          if (response.text) {
295            var result = JSON.parse(response.text);
296            // CHANGE
297            if(model.clear){
298              model.clear();
299              if (!model.set(result)) return false;
300              success(model);              
301            }else{
302              success(result);              
303            }
304          }
305          else success();
306
307        };
308
309        params['success'] = defaultSuccess;
310
311        var error = params['error'];
312
313        var defaultError = function(response) {
314          var result = response.text ? JSON.parse(response.text) : response;
315          (function(m, d) { error(d); }).call(StackMob, model, result);
316        }
317
318        params['error'] = defaultError;
319
320        var hash = {};
321        hash['url'] = params['url'];
322        hash['headers'] = params['headers'];
323        hash['success'] = params['success'];
324        hash['failure'] = params['error'];
325        hash['method'] = params['type'];
326        hash['data'] = params['data'];
327        // CHANGE
328        if (hash['method'] === "PUT" || hash['method'] === "POST") {
329          hash['headers']["Content-Type"] = "application/json"
330        }
331        
332        return jsOAuth.request(hash);
333      }
334      
335      function _makeSenchaAjaxCall(model, params) {
336        var success = params['success'];
337
338        var defaultSuccess = function(response, options) {
339          if (response.responseText) {
340            var result = JSON.parse(response.responseText);
341            model.clear();
342            if (!model.set(result)) return false;
343            success(model);
344          }
345          else success();
346
347        };
348
349        params['success'] = defaultSuccess;
350
351        var error = params['error'];
352
353        var defaultError = function(response, request) {
354          var result = response.responseText ? JSON.parse(response.responseText) : response;
355          (function(m, d) { error(d); }).call(StackMob, model, result);
356        }
357
358        params['error'] = defaultError;
359
360        var hash = {};
361        hash['url'] = params['url'];
362        hash['headers'] = params['headers'];
363        hash['params'] = params['data'];
364        hash['success'] = params['success'];
365        hash['failure'] = params['error'];
366        hash['disableCaching'] = false;
367        hash['method'] = params['type'];
368
369        return $.Ajax.request(hash);
370      }
371      
372      function _makeZeptoAjaxCall(model, params) {
373        var success = params['success'];
374
375        var defaultSuccess = function(response, result, xhr) {
376          if (response) {
377            var result = JSON.parse(response);
378            model.clear();
379            if (!model.set(result)) return false;
380            success(model);
381          }
382          else success();
383
384        };
385
386        params['success'] = defaultSuccess;
387
388        var error = params['error'];
389
390        var defaultError = function(response, request) {
391          var result = response.responseText ? JSON.parse(response.responseText) : response;
392          (function(m, d) { error(d); }).call(StackMob, model, result);
393        }
394
395        params['error'] = defaultError;
396
397        var hash = {};
398        hash['url'] = params['url'];
399        hash['headers'] = params['headers'];
400        hash['type'] = params['type'];
401        hash['data'] = params['data'];
402        hash['success'] = defaultSuccess;
403        hash['error'] = defaultError;
404
405        return $.ajax(hash);
406      }
407
408      function _makeJQueryAjaxCall(model, params) {
409
410        params['beforeSend'] = function(jqXHR, settings) {
411          jqXHR.setRequestHeader("Accept", settings['accepts']);
412          if (!_.isEmpty(settings['headers'])) {
413
414            for (key in settings['headers']) {
415              jqXHR.setRequestHeader(key, settings['headers'][key]);
416            }
417          }
418        };
419
420
421        var success = params['success'];
422
423        var defaultSuccess = function(model, response, xhr) {
424          var result;
425          if (model && model.toJSON) {
426            result = model;
427          } else if (model && (model.responseText || model.text)) {
428            var json = JSON.parse(model.responseText || model.text);
429            result = json;
430          } else if (model) {
431            result = model;
432          }
433
434          if (success) success(result);
435        };
436
437        params['success'] = defaultSuccess;
438
439        var err = params['error'];
440
441        params['error'] = function(jqXHR, textStatus, errorThrown) {
442          if (jqXHR.status == 302 && jqXHR.getResponseHeader("locations")) {
443            //we have a redirect to a new cluster
444            //console.log("We should move this to " + jqXHR.getResponseHeader("locations"));
445          }
446
447          var data;
448
449          if (jqXHR && (jqXHR.responseText || jqXHR.text)) {
450            var result = JSON.parse(jqXHR.responseText || jqXHR.text);
451            data = result;
452          }
453
454          (function(m, d) { err(d); }).call(StackMob, model, data);
455        }
456
457        return $.ajax(params);
458      }
459
460      //Determine what kind of call to make: GET, POST, PUT, DELETE
461      var type = StackMob.METHOD_MAP[method] || 'GET';
462
463      //Prepare query configuration
464      var params = _.extend({
465          type:         type,
466          dataType:     'json'
467      }, options);
468
469      params['data'] = params['data'] || {};
470
471      _prepareBaseURL(model, params);
472      _prepareHeaders(params, options);
473      _prepareRequestBody(method, params, options);
474      _prepareAjaxClientParams(params);
475
476
477      if (isSencha()) {
478        return _makeSenchaAjaxCall(model, params);
479      } else if (isZepto()) {
480      	return _makeZeptoAjaxCall(model, params);
481      } else {
482        return _makeOAuthAjaxCall(model, params);
483        // return _makeJQueryAjaxCall(model, params);
484      };
485    }
486
487  }); //end of StackMob
488
489
490	var createStackMobModel = function() {
491
492    /**
493     * Abstract Class representing a StackMob Model
494     */
495    StackMob.Model = Backbone.Model.extend({
496
497      urlRoot: StackMob['urlRoot'],
498
499      url: function() {
500        var base = StackMob['urlRoot'] || StackMob.urlError();
501        base += this.schemaName;
502        return base;
503      },
504
505      getPrimaryKeyField: function() {
506        return this.schemaName + '_id';
507      },
508
509      constructor: function() {
510        this.setIDAttribute(); //have to do this because I want to set this.id before this.set is called in default constructor
511        Backbone.Model.prototype.constructor.apply(this, arguments);
512      },
513
514
515      initialize: function(attributes, options) {
516        StackMob.getProperty(this, 'schemaName') || StackMob.throwError('A schemaName must be defined');
517        this.setIDAttribute();
518      },
519
520      setIDAttribute: function() {
521        this.idAttribute = this.getPrimaryKeyField();
522      },
523
524      parse: function(data, xhr) {
525        if (!data || (data && (!data['text'] || data['text'] == '') )) return data;
526
527        var attrs = JSON.parse(data['text']);
528
529        return attrs;
530      },
531
532
533      sync: function(method, model, options) {
534        StackMob.sync.call(this, method, this, options);
535      },
536
537      create: function(options) {
538        var newOptions = {};
539        newOptions[StackMob.FORCE_CREATE_REQUEST] = true;
540        _.extend(newOptions, options)
541        this.save(null, newOptions);
542      },
543
544      fetchExpanded: function(depth, options) {
545        if (depth < 0 || depth > 3) StackMob.throwError('Depth must be between 0 and 3 inclusive.');
546        var newOptions = {};
547        _.extend(newOptions, options);
548        newOptions['data'] = newOptions['data'] || {};
549        newOptions['data']['_expand'] = depth;
550
551        this.fetch(newOptions);
552      },
553
554      getAsModel: function(fieldName, model) {
555        var obj = this.get(fieldName);
556        if (!obj) return {};
557        else {
558          if (_.isArray(obj)) {
559            return _.map(obj, function(o) {
560              return new model(o);
561            });
562          } else {
563            return new model(obj);
564          }
565        }
566      },
567
568      appendAndCreate: function(fieldName, values, options) {
569        options = options || {};
570        options[StackMob.ARRAY_FIELDNAME] = fieldName;
571        options[StackMob.ARRAY_VALUES] = values;
572        StackMob.sync.call(this, 'appendAndCreate', this, options);
573      },
574
575      appendAndSave: function(fieldName, values, options) {
576        options = options || {};
577        options[StackMob.ARRAY_FIELDNAME] = fieldName;
578        options[StackMob.ARRAY_VALUES] = values;
579        StackMob.sync.call(this, 'appendAndSave', this, options);
580      },
581
582      deleteAndSave: function(fieldName, values, cascadeDelete, options) {
583        options = options || {};
584        options[StackMob.ARRAY_FIELDNAME] = fieldName;
585        options[StackMob.ARRAY_VALUES] = values;
586        options[StackMob.CASCADE_DELETE] = cascadeDelete;
587        StackMob.sync.call(this, 'deleteAndSave', this, options);
588      }
589
590    });
591
592  };
593
594  var createStackMobCollection = function() {
595    StackMob.Collection = Backbone.Collection.extend({
596      initialize: function() {
597        this.model || StackMob.throwError('Please specify a StackMobModel for this collection via.. var YourCollection = StackMobCollection.extend({ model: YourStackMobModelClass });');
598        this.schemaName = (new this.model()).schemaName;
599      },
600
601      url: function () {
602        var base = StackMob['urlRoot'] || StackMob.urlError();
603        base += this.schemaName;
604        return base;
605      },
606
607      parse: function(data, xhr) {
608        if (!data || (data && (!data['text'] || data['text'] == '') )) return data;
609
610        var attrs = JSON.parse(data['text']);
611        return attrs;
612      },
613
614      sync: function(method, model, options) {
615        StackMob.sync.call(this, method, this, options);
616      },
617
618      query: function(stackMobQuery, options) {
619        options = options || {};
620        _.extend(options, { query: stackMobQuery })
621        this.fetch(options);
622      },
623
624      create: function(model, options) {
625        var newOptions = {};
626        newOptions[StackMob.FORCE_CREATE_REQUEST] = true;
627        _.extend(newOptions, options);
628        Backbone.Collection.prototype.create.call(this, model, newOptions);
629      }
630    });
631  };
632
633	var createStackMobUserModel = function() {
634    /**
635     * User object
636     */
637    StackMob.User = StackMob.Model.extend({
638
639      idAttribute: StackMob['loginField'],
640
641      schemaName: 'user',
642
643      getPrimaryKeyField: function() { return StackMob.loginField; },
644
645      loginWithFacebookToken: function(facebookAccessToken, keepLoggedIn, options) {
646        options = options || {};
647        options['data'] = options['data'] || {};
648        _.extend(options['data'],
649          {
650            "fb_at": facebookAccessToken
651          });
652
653        (this.sync || Backbone.sync).call(this, "facebookLogin", this, options);
654      },
655
656      createUserWithFacebook: function(facebookAccessToken, options) {
657        options = options || {};
658        options['data'] = options['data'] || {};
659        _.extend(options['data'],
660          {
661            "fb_at": facebookAccessToken
662          });
663
664        options['data'][StackMob.loginField] = options[StackMob['loginField']] || this.get(StackMob['loginField']);
665
666
667        (this.sync || Backbone.sync).call(this, "createUserWithFacebook", this, options);
668      },
669
670      //Use after a user has logged in with a regular user account and you want to add Facebook to their account
671      linkUserWithFacebook: function(facebookAccessToken, options) {
672        options = options || {};
673        options['data'] = options['data'] || {};
674        _.extend(options['data'],
675          {
676            "fb_at": facebookAccessToken
677          });
678
679        (this.sync || Backbone.sync).call(this, "linkUserWithFacebook", this, options);
680      },
681
682      login: function(keepLoggedIn, options) {
683        options = options || {};
684        var remember = isNaN(keepLoggedIn) ? false : keepLoggedIn;
685        options['data'] = options['data'] || {};
686        options['data'][StackMob.loginField] = this.get(StackMob.loginField);
687        options['data'][StackMob.passwordField] = this.get(StackMob.passwordField);
688
689        (this.sync || Backbone.sync).call(this, "login", this, options);
690      },
691
692      logout: function(options) {
693        (this.sync || Backbone.sync).call(this, "logout", this, options);
694      }
695    });
696
697    /**
698     * Collection of users
699     */
700    StackMob.Users = StackMob.Collection.extend({
701      model: StackMob.User
702    });
703
704    /*
705     * Object to help users make StackMob Queries
706     *
707     * //Example query for users with age < 25, order by age ascending.  Return second set of 25 results.
708     * var q = new StackMob.Query();
709     * q.lt('age', 25).orderByAsc('age').setRange(25, 49);
710     */
711
712    StackMob.GeoPoint = function(lat, lon) {
713      if (_.isNumber(lat)) {
714        this.lat = lat;
715        this.lon = lon;
716      } else {
717        this.lat = lat['lat'];
718        this.lon = lat['lon'];
719      }
720
721    }
722
723    StackMob.GeoPoint.prototype.toJSON = function() {
724      return {
725        lat: this.lat,
726        lon: this.lon
727      };
728    }
729
730    StackMob.Collection.Query = function() {
731      this.params = {};
732      this.orderBy = [];
733      this.range = null;
734    }
735
736    //Give the StackMobQuery its methods
737    _.extend(StackMob.Collection.Query.prototype, {
738      addParam: function(key, value) {
739        this.params[key] = value;
740        return this;
741      },
742      equals: function(field, value) {
743        this.params[field] = value;
744        return this;
745      },
746      lt: function(field, value) {
747        this.params[field + '[lt]'] = value;
748        return this;
749      },
750      lte: function(field, value) {
751        this.params[field + '[lte]'] = value;
752        return this;
753      },
754      gt: function(field, value) {
755        this.params[field + '[gt]'] = value;
756        return this;
757      },
758      gte: function(field, value) {
759        this.params[field + '[gte]'] = value;
760        return this;
761      },
762      mustBeOneOf: function(field, value) {
763        var inValue = '';
764        if (_.isArray(value)) {
765          var newValue = '';
766          var size = value.length;
767          for (var i = 0; i < size; i++) {
768              inValue += value[i];
769              if (i + 1 < size) inValue += ',';
770            }
771        } else inValue = value;
772
773        this.params[field + '[in]'] = inValue;
774        return this;
775      },
776      orderAsc: function(field) {
777        this.orderBy.push(field + ':asc');
778        return this;
779      },
780      orderDesc: function(field) {
781        this.orderBy.push(field + ':desc');
782        return this;
783      },
784      setRange: function(start, end){
785        this.range = { 'start': start, 'end': end };
786        return this;
787      },
788      setExpand: function(depth) {
789        this.params['_expand'] = depth;
790        return this;
791      },
792      mustBeNear: function(field, smGeoPoint, distance) {
793        this.params[field + '[near]'] = smGeoPoint.lat + ',' + smGeoPoint.lon + ',' + distance;
794        return this;
795      },
796      mustBeNearMi: function(field, smGeoPoint, miles) {
797        this.mustBeNear(field, smGeoPoint, miles / StackMob.EARTH_RADIANS_MI);
798        return this;
799      },
800      mustBeNearKm: function(field, smGeoPoint, miles) {
801        this.mustBeNear(field, smGeoPoint, miles / StackMob.EARTH_RADIANS_KM);
802        return this;
803      },
804      isWithin: function(field, smGeoPoint, distance) {
805        this.params[field + '[within]'] = smGeoPoint.lat + ',' + smGeoPoint.lon + ',' + distance;
806        return this;
807      },
808      isWithinMi: function(field, smGeoPoint, distance) {
809        this.isWithin(field, smGeoPoint, distance / StackMob.EARTH_RADIANS_MI);
810        return this;
811      },
812      isWithinKm: function(field, smGeoPoint, distance) {
813        this.isWithin(field, smGeoPoint, distance / StackMob.EARTH_RADIANS_KM);
814        return this;
815      },
816      isWithinBox: function(field, smGeoPoint1, smGeoPoint2) {
817        this.params[field + '[within]'] = smGeoPoint1.lat + ',' + smGeoPoint1.lon + ',' + smGeoPoint2.lat + ',' + smGeoPoint2.lon;
818        return this;
819      }
820    }); //end extend StackMobQuery.prototype
821  };
822
823}).call(this);