2 * jqCouchDB - jQuery plugin for couchdb connections
3 * @requires jQuery > v1.0.3
4 * @requires couchDB >= v0.7.0a5575
6 * http://protoblogr.net
8 * Copyright (c) 2007 Jerry Jalava (protoblogr.net)
9 * Dual licensed under the MIT and GPL licenses:
10 * http://www.opensource.org/licenses/mit-license.php
11 * http://www.gnu.org/licenses/gpl.html
20 var dbc = $.jqCouch.connection('db');
21 dbc.exists('database_name');
23 if ($.jqCouch.connection('db').create('database_name').ok) {
24 alert("database created");
29 var dc = $.jqCouch.connection('doc');
30 var rev = dc.get('database/document1')._rev;
32 var doc = {_id:"0",a:1,b:1};
33 if ($.jqCouch.connection('doc').save('database_name', doc)._id !== false) {
34 alert("Created document with rev: "+doc._rev+", a="+doc.a);
37 //Get all documents from database. (With cache)
38 var dc = $.jqCouch.connection('doc');
39 dc.update_config('cache', true);
40 if (var total_documents = dc.all('database_name').total_rows) {
41 var all_documents = dc.all('database_name').rows;
43 //Get all documents from database. (Without cache)
44 var dc = $.jqCouch.connection('doc');
45 var all = dc.all('database_name');
46 if (all.total_rows > 0) {
47 var all_documents = all.rows;
52 var vc = $.jqCouch.connection('view');
53 if (vc.exists('database_name', 'event') !== false) {
54 alert("View "event" exists");
57 if ($.jqCouch.connection('view').exists('database_name', 'event/all') !== false) {
58 alert("View "event/all" exists");
61 More examples can be found from the testsuite
67 * Generates new connection instance
68 * @param {String} type Connection type to initiate
69 * @param {Mixed}(String/Function) map_fun Global result mapping function (Optional)
70 * @param {Object} config Global configuration for connection (Optional)
71 * @returns Connection handler
74 function jqCouch_connection(type, map_fun, config) {
75 var default_config = {
79 this.id = jQuery.jqCouch._generate_id();
81 this.map_fun = map_fun || null;
83 this.config = jQuery.extend({}, default_config, config);
87 this.destroy = function() {
91 if (typeof eval('jqCouch_'+this.type) == 'function') {
92 eval('var fn = eval(jqCouch_'+this.type+'); handler = fn.apply(fn, [this.map_fun, config, this]);');
95 handler.last_error = typeof handler.last_error == 'undefined' ? {} : handler.last_error;
96 handler.update_config = function(key, value) {
97 this.config[key] = value;
100 handler.last_results = typeof handler.last_results == 'undefined' ? {} : handler.last_results;
101 handler.purge_cache = function() {
102 handler.last_results = {};
112 * @param {Mixed}(String/Function) map_fun Global result mapping function (Optional)
113 * @param {Object} config Global configuration for connection (Optional)
114 * @param {Function} conn Connection constructor instance
115 * @see jqCouch_connection
116 * @returns Connection handler
119 function jqCouch_db(map_fun, config, conn) {
120 var default_config = {
123 this.config = jQuery.extend({}, default_config, config);
125 this.instance = conn;
128 //TODO: add chance to create if doesnt exist
129 this.exists = function(path, mf) {
130 if (typeof path == 'undefined') {
131 var path = this.config.path;
134 if (this.info(path, mf).db_name) {
141 this.info = function(path, mf) {
142 if (typeof path == 'undefined') {
143 var path = this.config.path;
145 if (path.substr(path.length-1,1) != '/') {
149 var map_fn = mf || map_fun;
153 var lr_key = encodeURIComponent(path).toString();
154 if ( typeof this.last_results[lr_key] != 'undefined'
155 && this.config.cache)
157 return this.last_results[lr_key];
161 url: _self.config.server_url + path,
163 global: _self.config.ajax_setup.global,
164 cache: _self.config.ajax_setup.cache,
167 contentType: 'application/json',
168 error: function(req) {
169 results = jQuery.extend({
171 }, jqCouch_map_error(req));
172 _self.last_error = results;
175 success: function(data) {
176 results = jqCouch_map_results(data, map_fn);
181 if (this.config.cache) {
182 this.last_results[lr_key] = results;
188 this.create = function(path, mf) {
189 if (typeof path == 'undefined') {
190 var path = this.config.path;
192 if (path.substr(path.length-1,1) != '/') {
196 var map_fn = mf || map_fun;
203 url: _self.config.server_url + path,
205 global: _self.config.ajax_setup.global,
206 cache: _self.config.ajax_setup.cache,
209 contentType: 'application/json',
210 error: function(req) {
211 results = jQuery.extend({
213 }, jqCouch_map_error(req));
214 _self.last_error = results;
217 success: function(data) {
218 results = jqCouch_map_results(data, map_fn);
226 this.del = function(path, mf) {
227 if (typeof path == 'undefined') {
228 var path = this.config.path;
230 if (path.substr(path.length-1,1) != '/') {
234 var map_fn = mf || map_fun;
241 url: _self.config.server_url + path,
243 global: _self.config.ajax_setup.global,
244 cache: _self.config.ajax_setup.cache,
247 contentType: 'application/json',
248 error: function(req) {
249 results = jQuery.extend({
251 }, jqCouch_map_error(req));
252 _self.last_error = results;
255 success: function(data) {
256 results = jqCouch_map_results(data, map_fn);
264 this.all = function(mf) {
269 var map_fn = mf || map_fun;
271 var lr_key = encodeURIComponent(_self.config.server_url + '_all_dbs').toString();
272 if ( typeof this.last_results[lr_key] != 'undefined'
273 && this.config.cache)
275 return this.last_results[lr_key];
279 url: _self.config.server_url + '_all_dbs',
281 global: _self.config.ajax_setup.global,
282 cache: _self.config.ajax_setup.cache,
285 contentType: 'application/json',
286 error: function(req) {
287 results = jqCouch_map_error(req);
288 _self.last_error = results;
291 success: function(data) {
292 results = jqCouch_map_results(data, map_fn);
297 if (this.config.cache) {
298 this.last_results[lr_key] = results;
304 this.restart = function(mf) {
309 var map_fn = mf || map_fun;
312 url: _self.config.server_url + '_restart',
314 global: _self.config.ajax_setup.global,
316 error: function(req) {
317 results = jQuery.extend({
319 }, jqCouch_map_error(req));
320 _self.last_error = results;
323 success: function(data) {
324 results = jqCouch_map_results(data, map_fn);
337 * Handles Doc actions
338 * @param {Mixed}(String/Function) map_fun Global result mapping function (Optional)
339 * @param {Object} config Global configuration for connection (Optional)
340 * @param {Function} conn Connection constructor instance
341 * @see jqCouch_connection
342 * @returns Connection handler
345 function jqCouch_doc(map_fun, config, conn) {
346 var default_config = {
349 this.config = jQuery.extend({}, default_config, config);
351 this.instance = conn;
354 this.get = function(path, args, mf) {
355 if ( typeof path == 'undefined'
358 var path = this.config.path;
361 var map_fn = mf || map_fun;
367 path += jQuery.jqCouch._generate_query_str(args);
369 var lr_key = encodeURIComponent(path).toString();
370 if ( typeof this.last_results[lr_key] != 'undefined'
371 && this.config.cache)
373 return this.last_results[lr_key];
377 url: _self.config.server_url + path,
379 global: _self.config.ajax_setup.global,
380 cache: _self.config.ajax_setup.cache,
383 contentType: 'application/json',
384 error: function(req) {
385 results = jQuery.extend({
387 }, jqCouch_map_error(req));
388 _self.last_error = results;
391 success: function(data) {
392 results = jqCouch_map_results(data, map_fn);
397 if (this.config.cache) {
398 this.last_results[lr_key] = results;
404 this.all = function(path, args, mf) {
405 if ( typeof path == 'undefined'
408 var path = this.config.path;
410 if (path.substr(path.length-1,1) != '/') {
414 path += jQuery.jqCouch._generate_query_str(args);
416 var map_fn = mf || map_fun;
422 var lr_key = encodeURIComponent(path).toString();
423 if ( typeof this.last_results[lr_key] != 'undefined'
424 && this.config.cache)
426 return this.last_results[lr_key];
430 url: _self.config.server_url + path,
432 global: _self.config.ajax_setup.global,
433 cache: _self.config.ajax_setup.cache,
436 contentType: 'application/json',
437 error: function(req) {
438 results = jQuery.extend({
440 }, jqCouch_map_error(req));
441 _self.last_error = results;
444 success: function(data) {
445 results = jqCouch_map_results(data, map_fn);
450 if (this.config.cache) {
451 this.last_results[lr_key] = results;
457 this.bulk_save = function(path, data, args, mf) {
458 if ( typeof path == 'undefined'
461 var path = this.config.path;
463 if (path.substr(path.length-1,1) != '/') {
466 path += '_bulk_docs';
467 path += jQuery.jqCouch._generate_query_str(args);
469 var map_fn = mf || map_fun;
477 url: _self.config.server_url + path,
479 global: _self.config.ajax_setup.global,
480 cache: _self.config.ajax_setup.cache,
483 contentType: 'application/json',
484 data: jQuery.jqCouch.toJSON(data),
486 error: function(req) {
487 results = jQuery.extend({
489 }, jqCouch_map_error(req));
490 _self.last_error = results;
493 success: function(data) {
494 results = jqCouch_map_results(data, map_fn);
502 this.save = function(path, data, mf) {
503 if ( typeof path == 'undefined'
506 var path = this.config.path;
508 if (path.substr(path.length-1,1) != '/') {
512 var map_fn = mf || map_fun;
515 if ( typeof data._id == 'undefined'
518 results = this.post(path, data, mf);
520 results = this.put(path + data._id, data, map_fn);
526 data._id = results.id;
527 data._rev = results.rev;
533 this.post = function(path, data, mf) {
534 if ( typeof path == 'undefined'
537 var path = this.config.path;
540 var map_fn = mf || map_fun;
547 url: _self.config.server_url + path,
549 global: _self.config.ajax_setup.global,
550 cache: _self.config.ajax_setup.cache,
553 contentType: 'application/json',
554 data: jQuery.jqCouch.toJSON(data),
556 error: function(req) {
557 results = jQuery.extend({
559 }, jqCouch_map_error(req));
560 _self.last_error = results;
563 success: function(data) {
564 results = jqCouch_map_results(data, map_fn);
572 this.put = function(path, data, mf) {
573 if ( typeof path == 'undefined'
576 var path = this.config.path;
579 var map_fn = mf || map_fun;
586 url: _self.config.server_url + path,
588 data: jQuery.jqCouch.toJSON(data),
590 global: _self.config.ajax_setup.global,
591 cache: _self.config.ajax_setup.cache,
594 contentType: 'application/json',
595 error: function(req) {
596 results = jQuery.extend({
598 }, jqCouch_map_error(req));
599 _self.last_error = results;
602 success: function(data) {
603 results = jqCouch_map_results(data, map_fn);
611 this.del = function(path, doc_or_rev, mf) {
612 if ( typeof path == 'undefined'
615 var path = this.config.path;
617 if ( typeof doc_or_rev == 'object'
618 && typeof doc_or_rev._id != 'undefined'
619 && typeof doc_or_rev._rev != 'undefined')
621 if (path.substr(path.length-1,1) != '/') {
624 path += doc_or_rev._id;
626 if ( typeof doc_or_rev._rev != 'undefined'
627 && doc_or_rev._rev != null)
629 path += '?rev=' + doc_or_rev._rev;
632 if (typeof doc_or_rev == 'string') {
633 path += '?rev=' + doc_or_rev;
636 var map_fn = mf || map_fun;
643 url: _self.config.server_url + path,
645 global: _self.config.ajax_setup.global,
646 cache: _self.config.ajax_setup.cache,
649 contentType: 'application/json',
650 error: function(req) {
651 results = jQuery.extend({
653 }, jqCouch_map_error(req));
654 _self.last_error = results;
657 success: function(data) {
658 results = jqCouch_map_results(data, map_fn);
663 if ( typeof doc_or_rev == 'object'
666 doc_or_rev._rev = results.rev;
667 doc_or_rev._deleted = true;
678 * Handles View actions
679 * @param {Mixed}(String/Function) map_fun Global result mapping function (Optional)
680 * @param {Object} config Global configuration for connection (Optional)
681 * @param {Function} conn Connection constructor instance
682 * @see jqCouch_connection
683 * @returns Connection handler
686 function jqCouch_view(map_fun, config, conn) {
687 var default_config = {
689 language: "text/javascript"
691 this.config = jQuery.extend({}, default_config, config);
693 this.instance = conn;
696 this.exists = function(path, view, mf) {
697 if (typeof path == 'undefined') {
698 var path = this.config.path;
701 if (typeof view == 'undefined') {
705 if (this.info(path, view, mf).views) {
712 this.info = function(path, view, mf) {
717 if (typeof view == 'undefined') {
721 if (typeof path == 'undefined') {
722 var path = this.config.path;
724 if (path.substr(path.length-1,1) != '/') {
729 var view_parts = false;
730 if (view.toString().match(/\//)) {
731 view_parts = view.split("/");
732 path += view_parts[0];
737 path += '?revs_info=true';
739 var map_fn = mf || map_fun;
741 var lr_key = encodeURIComponent(path).toString();
742 if ( typeof this.last_results[lr_key] != 'undefined'
743 && this.config.cache)
745 return this.last_results[lr_key];
749 url: _self.config.server_url + path,
751 global: _self.config.ajax_setup.global,
752 cache: _self.config.ajax_setup.cache,
755 contentType: 'application/json',
756 error: function(req) {
757 results = jQuery.extend({
760 }, jqCouch_map_error(req));
761 _self.last_error = results;
764 success: function(data) {
766 && typeof data.views[view_parts[1]] == 'undefined')
770 results = jqCouch_map_results(data, map_fn);
771 if (typeof results['views'] == 'undefined') {
772 results['views'] = false;
778 if (this.config.cache) {
779 this.last_results[lr_key] = results;
785 this.get = function(path, name, args, mf) {
790 if ( typeof path == 'undefined'
793 var path = this.config.path;
795 if (path.substr(path.length-1,1) != '/') {
798 if (! path.toString().match(/_view\//)) {
802 if (typeof name == 'undefined') {
806 if (! name.toString().match(/\//)) {
811 var map_fn = mf || map_fun;
813 path += jQuery.jqCouch._generate_query_str(args);
815 var lr_key = encodeURIComponent(path).toString();
816 if ( typeof this.last_results[lr_key] != 'undefined'
817 && this.config.cache)
819 return this.last_results[lr_key];
823 url: _self.config.server_url + path,
825 global: _self.config.ajax_setup.global,
826 cache: _self.config.ajax_setup.cache,
829 contentType: 'application/json',
830 error: function(req) {
831 results = jQuery.extend({
833 }, jqCouch_map_error(req));
834 _self.last_error = results;
837 success: function(data) {
838 results = jqCouch_map_results(data, map_fn);
843 if (this.config.cache) {
844 this.last_results[lr_key] = results;
850 this.save = function(path, data, mf) {
854 if (typeof data != 'object') {
858 if ( typeof path == 'undefined'
861 var path = this.config.path;
863 if (path.substr(path.length-1,1) != '/') {
867 if (typeof data._id == 'undefined') {
871 if (! data._id.toString().match(/_design/)) {
872 data._id = '_design/' + data._id;
875 if (typeof data['language'] == 'undefined') {
876 data.language = this.config.language;
879 for (var name in data.views) {
880 if (typeof(data.views[name]['toSource']) == 'function') {
881 data.views[name] = data.views[name].toSource();
883 data.views[name] = data.views[name].toString();
887 results = this.put(path + data._id, data, mf);
892 data._id = results.id;
893 data._rev = results.rev;
899 this.put = function(path, data, mf) {
900 if ( typeof path == 'undefined'
903 var path = this.config.path;
906 var map_fn = mf || map_fun;
913 url: _self.config.server_url + path,
915 data: jQuery.jqCouch.toJSON(data),
917 global: _self.config.ajax_setup.global,
918 cache: _self.config.ajax_setup.cache,
921 contentType: 'application/json',
922 error: function(req) {
923 results = jQuery.extend({
925 }, jqCouch_map_error(req));
926 _self.last_error = results;
929 success: function(data) {
930 results = jqCouch_map_results(data, map_fn);
938 this.del = function(path, doc_or_rev, mf) {
939 if ( typeof path == 'undefined'
942 var path = this.config.path;
944 if ( typeof doc_or_rev == 'object'
945 && typeof doc_or_rev._id != 'undefined'
946 && typeof doc_or_rev._rev != 'undefined')
948 if (path.substr(path.length-1,1) != '/') {
951 path += doc_or_rev._id;
952 path += '?rev=' + doc_or_rev._rev;
954 if (typeof doc_or_rev == 'string') {
955 path += '?rev=' + doc_or_rev;
958 var map_fn = mf || map_fun;
965 url: _self.config.server_url + path,
967 global: _self.config.ajax_setup.global,
968 cache: _self.config.ajax_setup.cache,
971 contentType: 'application/json',
972 error: function(req) {
973 results = jQuery.extend({
975 }, jqCouch_map_error(req));
976 _self.last_error = results;
979 success: function(data) {
980 results = jqCouch_map_results(data, map_fn);
985 if ( typeof doc_or_rev == 'object'
988 doc_or_rev._rev = results.rev;
989 doc_or_rev._deleted = true;
995 this.temp = function(path, map, args, mf) {
996 if ( typeof path == 'undefined'
999 var path = this.config.path;
1001 if (path.substr(path.length-1,1) != '/') {
1004 path += '_temp_view';
1005 path += jQuery.jqCouch._generate_query_str(args);
1007 var lr_key = encodeURIComponent(path).toString();
1008 if ( typeof this.last_results[lr_key] != 'undefined'
1009 && this.config.cache)
1011 return this.last_results[lr_key];
1014 var map_fn = mf || map_fun;
1016 if (typeof map == 'string') {
1020 if (typeof map['toSource'] == 'function') {
1021 map = map.toSource();
1023 map = map.toString();
1031 url: _self.config.server_url + path,
1033 global: _self.config.ajax_setup.global,
1034 cache: _self.config.ajax_setup.cache,
1038 contentType: 'application/javascript',
1040 error: function(req) {
1041 results = jQuery.extend({
1043 }, jqCouch_map_error(req));
1044 _self.last_error = results;
1047 success: function(data) {
1048 results = jqCouch_map_results(data, map_fn);
1053 if (this.config.cache) {
1054 this.last_results[lr_key] = results;
1065 * Default result wrapper
1066 * @param {Object} data Results from query
1067 * @param {Mixed}(String/Function) map result mapping function (Optional)
1068 * @see jqCouch_db, jqCouch_doc, jqCouch_view
1069 * @returns Finished results
1072 function jqCouch_map_results(data, map) {
1073 if (typeof map == 'undefined') {
1077 if (typeof map == 'function') {
1078 var res = map.apply(map, [data]);
1079 return jQuery.extend({}, res || {});
1082 if (typeof map == 'string') {
1083 var fn = eval('('+map+')');
1084 var res = fn.apply(fn, [data]);
1085 return jQuery.extend({}, res || {});
1088 return (data || {});
1093 * Default error wrapper
1094 * @param {Object} req XHR Request object
1095 * @see jqCouch_db, jqCouch_doc, jqCouch_view
1096 * @returns Rendered error
1099 function jqCouch_map_error(req) {
1102 response: eval("(" + req.responseText + ")"),
1111 _connection_types: [
1125 _generate_id: function() {
1126 var rk = Math.floor(Math.random()*4013);
1127 return (10016486 + (rk * 22423));
1130 _generate_query_str: function(qa) {
1132 if (typeof qa != 'undefined') {
1134 $.each(qa, function(k,v){
1135 if (typeof v != 'string') {
1136 qs += k + '=' + $.jqCouch.toJSON(v) + '&';
1138 qs += k + '=' + v + '&';
1141 qs = qs.substr(0, qs.length-1);
1147 parseJSON: function(json_str) {
1149 return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
1150 json_str.replace(/"(\\.|[^"\\])*"/g, ''))) &&
1151 eval('(' + json_str + ')');
1156 toJSON: function (item, item_type) {
1168 var a = ['['], b, f, i, l = x.length, v;
1169 for (i = 0; i < l; i += 1) {
1172 if (typeof v == 'string') {
1183 bool: function (x) {
1190 return isFinite(x) ? String(x) : 'null';
1194 if (x instanceof Array) {
1197 var a = ['{'], b, f, i, v;
1201 if (typeof v == 'string') {
1205 a.push(s.str(i), ':', v);
1215 if (/["\\\x00-\x1f]/.test(x)) {
1216 x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
1223 Math.floor(c / 16).toString(16) +
1224 (c % 16).toString(16);
1227 return '"' + x + '"';
1230 var conv = function (x) {
1231 var itemtype = typeof x;
1254 var itemtype = item_type || typeof item;
1272 return s.bool(item);
1275 throw("Unknown type for $.jqCouch.toJSON");
1281 * Defines globally used default settings for jqCouch
1282 * @param {Object} defaults Config dictionary
1283 * @see $.jqCouch._default_config
1285 set_defaults: function(defaults) {
1286 $.jqCouch._default_config = $.extend({}, $.jqCouch._default_config, defaults || {});
1291 * Initiates new connection and returns it.
1292 * Possible parameter combinations:
1293 * type OR type, map_fun OR type, config OR type, map_fun, config
1294 * @param {String} type Type of connection to initiate
1295 * @param {Mixed}(String/Function) map_fun Global result mapping function
1296 * @param {Object} config Global configuration for connection
1297 * @see jqCouch_connection
1298 * @returns Connection handler
1301 connection: function() {
1302 if (arguments.length <= 0) {
1306 if (typeof $.jqCouch._connections != 'object') {
1307 $.jqCouch._connections = {};
1312 var user_config = {};
1314 if ($.inArray(arguments[0], $.jqCouch._connection_types) != -1) {
1315 type = arguments[0];
1320 if (arguments.length > 1) {
1321 if (typeof arguments[1] == 'object') {
1322 user_config = arguments[1];
1324 map_fun = arguments[1];
1328 if ( arguments.length == 3
1329 && typeof arguments[2] == 'object')
1331 user_config = arguments[2];
1334 var config = $.extend({}, $.jqCouch._default_config, user_config);
1336 var connection = new jqCouch_connection(type, map_fun, config);
1337 if (! connection.instance.inited) {
1341 $.jqCouch._connections[connection.id] = connection;
1346 destroy_connection: function(id) {
1347 if ( typeof $.jqCouch._connections[id] != 'undefined'
1348 || $.jqCouch._connections[id] != 'null')
1350 $.jqCouch._connections[id].destroy();
1351 $.jqCouch._connections[id] = null;
1353 $.jqCouch._connections = $.grep($.jqCouch._connections, function(n,i){