soca 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/HISTORY +3 -0
  4. data/LICENSE +20 -0
  5. data/README.md +89 -0
  6. data/Rakefile +58 -0
  7. data/bin/soca +5 -0
  8. data/lib/soca.rb +34 -0
  9. data/lib/soca/cli.rb +189 -0
  10. data/lib/soca/plugin.rb +31 -0
  11. data/lib/soca/plugins/compass.rb +24 -0
  12. data/lib/soca/plugins/jim.rb +19 -0
  13. data/lib/soca/pusher.rb +186 -0
  14. data/lib/soca/templates/Jimfile +12 -0
  15. data/lib/soca/templates/config.js.erb +4 -0
  16. data/lib/soca/templates/couchapprc.erb +10 -0
  17. data/lib/soca/templates/css/screen.css +1 -0
  18. data/lib/soca/templates/db/views/by_type/map.js +3 -0
  19. data/lib/soca/templates/hooks/before_build.rb +2 -0
  20. data/lib/soca/templates/index.html.erb +17 -0
  21. data/lib/soca/templates/js/app.js +12 -0
  22. data/lib/soca/templates/js/vendor/jquery-1.4.2.js +6240 -0
  23. data/lib/soca/templates/js/vendor/jquery.couch-0.11.js +668 -0
  24. data/lib/soca/templates/js/vendor/sammy-0.6.1.js +1809 -0
  25. data/lib/soca/templates/js/vendor/sammy.couch-0.1.0.js +122 -0
  26. data/lib/soca/templates/js/vendor/sha1.js +202 -0
  27. data/soca.gemspec +107 -0
  28. data/test/helper.rb +36 -0
  29. data/test/test_soca_cli.rb +120 -0
  30. data/test/test_soca_pusher.rb +79 -0
  31. data/test/testapp/.couchapprc +10 -0
  32. data/test/testapp/Jimfile +11 -0
  33. data/test/testapp/config.js +11 -0
  34. data/test/testapp/css/app.css +3 -0
  35. data/test/testapp/db/views/recent/map.js +5 -0
  36. data/test/testapp/hooks/before_build.rb +1 -0
  37. data/test/testapp/index.html +11 -0
  38. data/test/testapp/js/app.js +5 -0
  39. data/test/testapp/js/bundled.js +8544 -0
  40. data/test/testapp/js/vendor/jquery-1.4.2.js +6240 -0
  41. data/test/testapp/js/vendor/json2.js +478 -0
  42. data/test/testapp/js/vendor/sammy-0.5.4.js +1403 -0
  43. data/test/testapp/js/vendor/sammy.mustache-0.5.4.js +415 -0
  44. data/test/testapp/templates/index.mustache +1 -0
  45. metadata +205 -0
@@ -0,0 +1,668 @@
1
+ // Licensed under the Apache License, Version 2.0 (the "License"); you may not
2
+ // use this file except in compliance with the License. You may obtain a copy of
3
+ // the License at
4
+ //
5
+ // http://www.apache.org/licenses/LICENSE-2.0
6
+ //
7
+ // Unless required by applicable law or agreed to in writing, software
8
+ // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9
+ // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10
+ // License for the specific language governing permissions and limitations under
11
+ // the License.
12
+
13
+ (function($) {
14
+ $.couch = $.couch || {};
15
+
16
+ function encodeDocId(docID) {
17
+ var parts = docID.split("/");
18
+ if (parts[0] == "_design") {
19
+ parts.shift();
20
+ return "_design/" + encodeURIComponent(parts.join('/'));
21
+ }
22
+ return encodeURIComponent(docID);
23
+ };
24
+
25
+ function prepareUserDoc(user_doc, new_password) {
26
+ if (typeof hex_sha1 == "undefined") {
27
+ alert("creating a user doc requires sha1.js to be loaded in the page");
28
+ return;
29
+ }
30
+ var user_prefix = "org.couchdb.user:";
31
+ user_doc._id = user_doc._id || user_prefix + user_doc.name;
32
+ if (new_password) {
33
+ // handle the password crypto
34
+ user_doc.salt = $.couch.newUUID();
35
+ user_doc.password_sha = hex_sha1(new_password + user_doc.salt);
36
+ }
37
+ user_doc.type = "user";
38
+ if (!user_doc.roles) {
39
+ user_doc.roles = []
40
+ }
41
+ return user_doc;
42
+ };
43
+
44
+ var uuidCache = [];
45
+
46
+ $.extend($.couch, {
47
+ urlPrefix: '',
48
+ activeTasks: function(options) {
49
+ ajax(
50
+ {url: this.urlPrefix + "/_active_tasks"},
51
+ options,
52
+ "Active task status could not be retrieved"
53
+ );
54
+ },
55
+
56
+ allDbs: function(options) {
57
+ ajax(
58
+ {url: this.urlPrefix + "/_all_dbs"},
59
+ options,
60
+ "An error occurred retrieving the list of all databases"
61
+ );
62
+ },
63
+
64
+ config: function(options, section, option, value) {
65
+ var req = {url: this.urlPrefix + "/_config/"};
66
+ if (section) {
67
+ req.url += encodeURIComponent(section) + "/";
68
+ if (option) {
69
+ req.url += encodeURIComponent(option);
70
+ }
71
+ }
72
+ if (value === null) {
73
+ req.type = "DELETE";
74
+ } else if (value !== undefined) {
75
+ req.type = "PUT";
76
+ req.data = toJSON(value);
77
+ req.contentType = "application/json";
78
+ req.processData = false
79
+ }
80
+
81
+ ajax(req, options,
82
+ "An error occurred retrieving/updating the server configuration"
83
+ );
84
+ },
85
+
86
+ session: function(options) {
87
+ options = options || {};
88
+ $.ajax({
89
+ type: "GET", url: this.urlPrefix + "/_session",
90
+ complete: function(req) {
91
+ var resp = $.httpData(req, "json");
92
+ if (req.status == 200) {
93
+ if (options.success) options.success(resp);
94
+ } else if (options.error) {
95
+ options.error(req.status, resp.error, resp.reason);
96
+ } else {
97
+ alert("An error occurred getting session info: " + resp.reason);
98
+ }
99
+ }
100
+ });
101
+ },
102
+
103
+ userDb : function(callback) {
104
+ $.couch.session({
105
+ success : function(resp) {
106
+ var userDb = $.couch.db(resp.info.authentication_db);
107
+ callback(userDb);
108
+ }
109
+ });
110
+ },
111
+
112
+ signup: function(user_doc, password, options) {
113
+ options = options || {};
114
+ // prepare user doc based on name and password
115
+ user_doc = prepareUserDoc(user_doc, password);
116
+ $.couch.userDb(function(db) {
117
+ db.saveDoc(user_doc, options);
118
+ })
119
+ },
120
+
121
+ login: function(options) {
122
+ options = options || {};
123
+ $.ajax({
124
+ type: "POST", url: this.urlPrefix + "/_session", dataType: "json",
125
+ data: {name: options.name, password: options.password},
126
+ complete: function(req) {
127
+ var resp = $.httpData(req, "json");
128
+ if (req.status == 200) {
129
+ if (options.success) options.success(resp);
130
+ } else if (options.error) {
131
+ options.error(req.status, resp.error, resp.reason);
132
+ } else {
133
+ alert("An error occurred logging in: " + resp.reason);
134
+ }
135
+ }
136
+ });
137
+ },
138
+ logout: function(options) {
139
+ options = options || {};
140
+ $.ajax({
141
+ type: "DELETE", url: this.urlPrefix + "/_session", dataType: "json",
142
+ username : "_", password : "_",
143
+ complete: function(req) {
144
+ var resp = $.httpData(req, "json");
145
+ if (req.status == 200) {
146
+ if (options.success) options.success(resp);
147
+ } else if (options.error) {
148
+ options.error(req.status, resp.error, resp.reason);
149
+ } else {
150
+ alert("An error occurred logging out: " + resp.reason);
151
+ }
152
+ }
153
+ });
154
+ },
155
+
156
+ db: function(name, db_opts) {
157
+ db_opts = db_opts || {};
158
+ var rawDocs = {};
159
+ function maybeApplyVersion(doc) {
160
+ if (doc._id && doc._rev && rawDocs[doc._id] && rawDocs[doc._id].rev == doc._rev) {
161
+ // todo: can we use commonjs require here?
162
+ if (typeof Base64 == "undefined") {
163
+ alert("please include /_utils/script/base64.js in the page for base64 support");
164
+ return false;
165
+ } else {
166
+ doc._attachments = doc._attachments || {};
167
+ doc._attachments["rev-"+doc._rev.split("-")[0]] = {
168
+ content_type :"application/json",
169
+ data : Base64.encode(rawDocs[doc._id].raw)
170
+ }
171
+ return true;
172
+ }
173
+ }
174
+ };
175
+ return {
176
+ name: name,
177
+ uri: this.urlPrefix + "/" + encodeURIComponent(name) + "/",
178
+
179
+ compact: function(options) {
180
+ $.extend(options, {successStatus: 202});
181
+ ajax({
182
+ type: "POST", url: this.uri + "_compact",
183
+ data: "", processData: false
184
+ },
185
+ options,
186
+ "The database could not be compacted"
187
+ );
188
+ },
189
+ viewCleanup: function(options) {
190
+ $.extend(options, {successStatus: 202});
191
+ ajax({
192
+ type: "POST", url: this.uri + "_view_cleanup",
193
+ data: "", processData: false
194
+ },
195
+ options,
196
+ "The views could not be cleaned up"
197
+ );
198
+ },
199
+ compactView: function(groupname, options) {
200
+ $.extend(options, {successStatus: 202});
201
+ ajax({
202
+ type: "POST", url: this.uri + "_compact/" + groupname,
203
+ data: "", processData: false
204
+ },
205
+ options,
206
+ "The view could not be compacted"
207
+ );
208
+ },
209
+ create: function(options) {
210
+ $.extend(options, {successStatus: 201});
211
+ ajax({
212
+ type: "PUT", url: this.uri, contentType: "application/json",
213
+ data: "", processData: false
214
+ },
215
+ options,
216
+ "The database could not be created"
217
+ );
218
+ },
219
+ drop: function(options) {
220
+ ajax(
221
+ {type: "DELETE", url: this.uri},
222
+ options,
223
+ "The database could not be deleted"
224
+ );
225
+ },
226
+ info: function(options) {
227
+ ajax(
228
+ {url: this.uri},
229
+ options,
230
+ "Database information could not be retrieved"
231
+ );
232
+ },
233
+ changes: function(since, options) {
234
+ options = options || {};
235
+ // set up the promise object within a closure for this handler
236
+ var timeout = 100, db = this, active = true,
237
+ listeners = [],
238
+ promise = {
239
+ onChange : function(fun) {
240
+ listeners.push(fun);
241
+ },
242
+ stop : function() {
243
+ active = false;
244
+ }
245
+ };
246
+ // call each listener when there is a change
247
+ function triggerListeners(resp) {
248
+ $.each(listeners, function() {
249
+ this(resp);
250
+ });
251
+ };
252
+ // when there is a change, call any listeners, then check for another change
253
+ options.success = function(resp) {
254
+ timeout = 100;
255
+ if (active) {
256
+ since = resp.last_seq;
257
+ triggerListeners(resp);
258
+ getChangesSince();
259
+ };
260
+ };
261
+ options.error = function() {
262
+ if (active) {
263
+ setTimeout(getChangesSince, timeout);
264
+ timeout = timeout * 2;
265
+ }
266
+ };
267
+ // actually make the changes request
268
+ function getChangesSince() {
269
+ var opts = $.extend({heartbeat : 10 * 1000}, options, {
270
+ feed : "longpoll",
271
+ since : since
272
+ });
273
+ ajax(
274
+ {url: db.uri + "_changes"+encodeOptions(opts)},
275
+ options,
276
+ "Error connecting to "+db.uri+"/_changes."
277
+ );
278
+ }
279
+ // start the first request
280
+ if (since) {
281
+ getChangesSince();
282
+ } else {
283
+ db.info({
284
+ success : function(info) {
285
+ since = info.update_seq;
286
+ getChangesSince();
287
+ }
288
+ });
289
+ }
290
+ return promise;
291
+ },
292
+ allDocs: function(options) {
293
+ var type = "GET";
294
+ var data = null;
295
+ if (options["keys"]) {
296
+ type = "POST";
297
+ var keys = options["keys"];
298
+ delete options["keys"];
299
+ data = toJSON({ "keys": keys });
300
+ }
301
+ ajax({
302
+ type: type,
303
+ data: data,
304
+ url: this.uri + "_all_docs" + encodeOptions(options)
305
+ },
306
+ options,
307
+ "An error occurred retrieving a list of all documents"
308
+ );
309
+ },
310
+ allDesignDocs: function(options) {
311
+ this.allDocs($.extend({startkey:"_design", endkey:"_design0"}, options));
312
+ },
313
+ allApps: function(options) {
314
+ options = options || {};
315
+ var self = this;
316
+ if (options.eachApp) {
317
+ this.allDesignDocs({
318
+ success: function(resp) {
319
+ $.each(resp.rows, function() {
320
+ self.openDoc(this.id, {
321
+ success: function(ddoc) {
322
+ var index, appPath, appName = ddoc._id.split('/');
323
+ appName.shift();
324
+ appName = appName.join('/');
325
+ index = ddoc.couchapp && ddoc.couchapp.index;
326
+ if (index) {
327
+ appPath = ['', name, ddoc._id, index].join('/');
328
+ } else if (ddoc._attachments && ddoc._attachments["index.html"]) {
329
+ appPath = ['', name, ddoc._id, "index.html"].join('/');
330
+ }
331
+ if (appPath) options.eachApp(appName, appPath, ddoc);
332
+ }
333
+ });
334
+ });
335
+ }
336
+ });
337
+ } else {
338
+ alert("Please provide an eachApp function for allApps()");
339
+ }
340
+ },
341
+ openDoc: function(docId, options, ajaxOptions) {
342
+ options = options || {};
343
+ if (db_opts.attachPrevRev || options.attachPrevRev) {
344
+ $.extend(options, {
345
+ beforeSuccess : function(req, doc) {
346
+ rawDocs[doc._id] = {
347
+ rev : doc._rev,
348
+ raw : req.responseText
349
+ };
350
+ }
351
+ });
352
+ } else {
353
+ $.extend(options, {
354
+ beforeSuccess : function(req, doc) {
355
+ if (doc["jquery.couch.attachPrevRev"]) {
356
+ rawDocs[doc._id] = {
357
+ rev : doc._rev,
358
+ raw : req.responseText
359
+ };
360
+ }
361
+ }
362
+ });
363
+ }
364
+ ajax({url: this.uri + encodeDocId(docId) + encodeOptions(options)},
365
+ options,
366
+ "The document could not be retrieved",
367
+ ajaxOptions
368
+ );
369
+ },
370
+ saveDoc: function(doc, options) {
371
+ options = options || {};
372
+ var db = this;
373
+ var beforeSend = fullCommit(options);
374
+ if (doc._id === undefined) {
375
+ var method = "POST";
376
+ var uri = this.uri;
377
+ } else {
378
+ var method = "PUT";
379
+ var uri = this.uri + encodeDocId(doc._id);
380
+ }
381
+ var versioned = maybeApplyVersion(doc);
382
+ $.ajax({
383
+ type: method, url: uri + encodeOptions(options),
384
+ contentType: "application/json",
385
+ dataType: "json", data: toJSON(doc),
386
+ beforeSend : beforeSend,
387
+ complete: function(req) {
388
+ var resp = $.httpData(req, "json");
389
+ if (req.status == 200 || req.status == 201 || req.status == 202) {
390
+ doc._id = resp.id;
391
+ doc._rev = resp.rev;
392
+ if (versioned) {
393
+ db.openDoc(doc._id, {
394
+ attachPrevRev : true,
395
+ success : function(d) {
396
+ doc._attachments = d._attachments;
397
+ if (options.success) options.success(resp);
398
+ }
399
+ });
400
+ } else {
401
+ if (options.success) options.success(resp);
402
+ }
403
+ } else if (options.error) {
404
+ options.error(req.status, resp.error, resp.reason);
405
+ } else {
406
+ alert("The document could not be saved: " + resp.reason);
407
+ }
408
+ }
409
+ });
410
+ },
411
+ bulkSave: function(docs, options) {
412
+ var beforeSend = fullCommit(options);
413
+ $.extend(options, {successStatus: 201, beforeSend : beforeSend});
414
+ ajax({
415
+ type: "POST",
416
+ url: this.uri + "_bulk_docs" + encodeOptions(options),
417
+ contentType: "application/json", data: toJSON(docs)
418
+ },
419
+ options,
420
+ "The documents could not be saved"
421
+ );
422
+ },
423
+ removeDoc: function(doc, options) {
424
+ ajax({
425
+ type: "DELETE",
426
+ url: this.uri +
427
+ encodeDocId(doc._id) +
428
+ encodeOptions({rev: doc._rev})
429
+ },
430
+ options,
431
+ "The document could not be deleted"
432
+ );
433
+ },
434
+ bulkRemove: function(docs, options){
435
+ docs.docs = $.each(
436
+ docs.docs, function(i, doc){
437
+ doc._deleted = true;
438
+ }
439
+ );
440
+ $.extend(options, {successStatus: 201});
441
+ ajax({
442
+ type: "POST",
443
+ url: this.uri + "_bulk_docs" + encodeOptions(options),
444
+ data: toJSON(docs)
445
+ },
446
+ options,
447
+ "The documents could not be deleted"
448
+ );
449
+ },
450
+ copyDoc: function(docId, options, ajaxOptions) {
451
+ ajaxOptions = $.extend(ajaxOptions, {
452
+ complete: function(req) {
453
+ var resp = $.httpData(req, "json");
454
+ if (req.status == 201) {
455
+ if (options.success) options.success(resp);
456
+ } else if (options.error) {
457
+ options.error(req.status, resp.error, resp.reason);
458
+ } else {
459
+ alert("The document could not be copied: " + resp.reason);
460
+ }
461
+ }
462
+ });
463
+ ajax({
464
+ type: "COPY",
465
+ url: this.uri + encodeDocId(docId)
466
+ },
467
+ options,
468
+ "The document could not be copied",
469
+ ajaxOptions
470
+ );
471
+ },
472
+ query: function(mapFun, reduceFun, language, options) {
473
+ language = language || "javascript";
474
+ if (typeof(mapFun) !== "string") {
475
+ mapFun = mapFun.toSource ? mapFun.toSource() : "(" + mapFun.toString() + ")";
476
+ }
477
+ var body = {language: language, map: mapFun};
478
+ if (reduceFun != null) {
479
+ if (typeof(reduceFun) !== "string")
480
+ reduceFun = reduceFun.toSource ? reduceFun.toSource() : "(" + reduceFun.toString() + ")";
481
+ body.reduce = reduceFun;
482
+ }
483
+ ajax({
484
+ type: "POST",
485
+ url: this.uri + "_temp_view" + encodeOptions(options),
486
+ contentType: "application/json", data: toJSON(body)
487
+ },
488
+ options,
489
+ "An error occurred querying the database"
490
+ );
491
+ },
492
+ list: function(list, view, options) {
493
+ var list = list.split('/');
494
+ var options = options || {};
495
+ var type = 'GET';
496
+ var data = null;
497
+ if (options['keys']) {
498
+ type = 'POST';
499
+ var keys = options['keys'];
500
+ delete options['keys'];
501
+ data = toJSON({'keys': keys });
502
+ }
503
+ ajax({
504
+ type: type,
505
+ data: data,
506
+ url: this.uri + '_design/' + list[0] +
507
+ '/_list/' + list[1] + '/' + view + encodeOptions(options)
508
+ },
509
+ options, 'An error occured accessing the list'
510
+ );
511
+ },
512
+ view: function(name, options) {
513
+ var name = name.split('/');
514
+ var options = options || {};
515
+ var type = "GET";
516
+ var data= null;
517
+ if (options["keys"]) {
518
+ type = "POST";
519
+ var keys = options["keys"];
520
+ delete options["keys"];
521
+ data = toJSON({ "keys": keys });
522
+ }
523
+ ajax({
524
+ type: type,
525
+ data: data,
526
+ url: this.uri + "_design/" + name[0] +
527
+ "/_view/" + name[1] + encodeOptions(options)
528
+ },
529
+ options, "An error occurred accessing the view"
530
+ );
531
+ },
532
+ getDbProperty: function(propName, options, ajaxOptions) {
533
+ ajax({url: this.uri + propName + encodeOptions(options)},
534
+ options,
535
+ "The property could not be retrieved",
536
+ ajaxOptions
537
+ );
538
+ },
539
+
540
+ setDbProperty: function(propName, propValue, options, ajaxOptions) {
541
+ ajax({
542
+ type: "PUT",
543
+ url: this.uri + propName + encodeOptions(options),
544
+ data : JSON.stringify(propValue)
545
+ },
546
+ options,
547
+ "The property could not be updated",
548
+ ajaxOptions
549
+ );
550
+ }
551
+ };
552
+ },
553
+
554
+ encodeDocId: encodeDocId,
555
+
556
+ info: function(options) {
557
+ ajax(
558
+ {url: this.urlPrefix + "/"},
559
+ options,
560
+ "Server information could not be retrieved"
561
+ );
562
+ },
563
+
564
+ replicate: function(source, target, ajaxOptions, repOpts) {
565
+ repOpts = $.extend({source: source, target: target}, repOpts);
566
+ if (repOpts.continuous) {
567
+ ajaxOptions.successStatus = 202;
568
+ }
569
+ ajax({
570
+ type: "POST", url: this.urlPrefix + "/_replicate",
571
+ data: JSON.stringify(repOpts),
572
+ contentType: "application/json"
573
+ },
574
+ ajaxOptions,
575
+ "Replication failed"
576
+ );
577
+ },
578
+
579
+ newUUID: function(cacheNum) {
580
+ if (cacheNum === undefined) {
581
+ cacheNum = 1;
582
+ }
583
+ if (!uuidCache.length) {
584
+ ajax({url: this.urlPrefix + "/_uuids", data: {count: cacheNum}, async: false}, {
585
+ success: function(resp) {
586
+ uuidCache = resp.uuids
587
+ }
588
+ },
589
+ "Failed to retrieve UUID batch."
590
+ );
591
+ }
592
+ return uuidCache.shift();
593
+ }
594
+ });
595
+
596
+ function ajax(obj, options, errorMessage, ajaxOptions) {
597
+ options = $.extend({successStatus: 200}, options);
598
+ ajaxOptions = $.extend({contentType: "application/json"}, ajaxOptions);
599
+ errorMessage = errorMessage || "Unknown error";
600
+ $.ajax($.extend($.extend({
601
+ type: "GET", dataType: "json", cache : !$.browser.msie,
602
+ beforeSend: function(xhr){
603
+ if(ajaxOptions && ajaxOptions.headers){
604
+ for (var header in ajaxOptions.headers){
605
+ xhr.setRequestHeader(header, ajaxOptions.headers[header]);
606
+ }
607
+ }
608
+ },
609
+ complete: function(req) {
610
+ try {
611
+ var resp = $.httpData(req, "json");
612
+ } catch(e) {
613
+ if (options.error) {
614
+ options.error(req.status, req, e);
615
+ } else {
616
+ alert(errorMessage + ": " + e);
617
+ }
618
+ return;
619
+ }
620
+ if (options.ajaxStart) {
621
+ options.ajaxStart(resp);
622
+ }
623
+ if (req.status == options.successStatus) {
624
+ if (options.beforeSuccess) options.beforeSuccess(req, resp);
625
+ if (options.success) options.success(resp);
626
+ } else if (options.error) {
627
+ options.error(req.status, resp && resp.error || errorMessage, resp && resp.reason || "no response");
628
+ } else {
629
+ alert(errorMessage + ": " + resp.reason);
630
+ }
631
+ }
632
+ }, obj), ajaxOptions));
633
+ }
634
+
635
+ function fullCommit(options) {
636
+ var options = options || {};
637
+ if (typeof options.ensure_full_commit !== "undefined") {
638
+ var commit = options.ensure_full_commit;
639
+ delete options.ensure_full_commit;
640
+ return function(xhr) {
641
+ xhr.setRequestHeader("X-Couch-Full-Commit", commit.toString());
642
+ };
643
+ }
644
+ };
645
+
646
+ // Convert a options object to an url query string.
647
+ // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"'
648
+ function encodeOptions(options) {
649
+ var buf = [];
650
+ if (typeof(options) === "object" && options !== null) {
651
+ for (var name in options) {
652
+ if ($.inArray(name, ["error", "success", "beforeSuccess", "ajaxStart"]) >= 0)
653
+ continue;
654
+ var value = options[name];
655
+ if ($.inArray(name, ["key", "startkey", "endkey"]) >= 0) {
656
+ value = toJSON(value);
657
+ }
658
+ buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
659
+ }
660
+ }
661
+ return buf.length ? "?" + buf.join("&") : "";
662
+ }
663
+
664
+ function toJSON(obj) {
665
+ return obj !== null ? JSON.stringify(obj) : null;
666
+ }
667
+
668
+ })(jQuery);