├── _id ├── language ├── couchapp.json ├── _attachments ├── images │ └── icon.png ├── index.html ├── style │ └── main.css └── script │ ├── app.js │ ├── sha1.js │ ├── json2.js │ └── jquery.couch.js ├── vendor └── couchapp │ ├── metadata.json │ └── _attachments │ ├── jquery.couchForm.js │ ├── jquery.couch.app.util.js │ ├── jquery.couchProfile.js │ ├── jquery.couchLogin.js │ ├── jquery.couch.app.js │ ├── md5.js │ └── jquery.mustache.js ├── views └── recent-items │ └── map.js └── README.md /_id: -------------------------------------------------------------------------------- 1 | _design/grocery -------------------------------------------------------------------------------- /language: -------------------------------------------------------------------------------- 1 | javascript -------------------------------------------------------------------------------- /couchapp.json: -------------------------------------------------------------------------------- 1 | {"name": "Grocery", "description": "Sync your grocery list."} -------------------------------------------------------------------------------- /_attachments/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jchris/GrocerySync-CouchApp/master/_attachments/images/icon.png -------------------------------------------------------------------------------- /vendor/couchapp/metadata.json: -------------------------------------------------------------------------------- 1 | {"name": "couchapp", "fetch_uri": "git://github.com/couchapp/couchapp.git", "description": "official couchapp vendor"} -------------------------------------------------------------------------------- /views/recent-items/map.js: -------------------------------------------------------------------------------- 1 | function(doc) { 2 | if (doc.created_at) { 3 | var date = new Date(doc.created_at.replace(/-/g, "/")); 4 | 5 | emit(((date.getTime() && date) || new Date(doc.created_at)), { 6 | text:doc.text, 7 | check: doc.check, 8 | id : doc._id 9 | }); 10 | } 11 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Generated CouchApp 2 | 3 | This is meant to be an example CouchApp and to ship with most of the CouchApp goodies. 4 | 5 | Install with 6 | 7 | couchapp push . http://localhost:5984/grocery-sync 8 | 9 | or (if you have security turned on) 10 | 11 | couchapp push . http://myname:mypass@localhost:5984/proto 12 | 13 | You can also create this app by running 14 | 15 | couchapp generate proto && cd proto 16 | couchapp push . http://localhost:5984/proto 17 | 18 | ## Todo 19 | 20 | * factor CouchApp Commonjs to jquery.couch.require.js 21 | * use $.couch.app in app.js 22 | 23 | ## License 24 | 25 | Apache 2.0 -------------------------------------------------------------------------------- /_attachments/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |Hello {{nickname}}!
', 18 | newProfile : '' 19 | }; 20 | 21 | $.fn.couchProfile = function(session, opts) { 22 | opts = opts || {}; 23 | var templates = $.couchProfile.templates; 24 | var userCtx = session.userCtx; 25 | var widget = $(this); 26 | // load the profile from the user doc 27 | var db = $.couch.db(session.info.authentication_db); 28 | var userDocId = "org.couchdb.user:"+userCtx.name; 29 | db.openDoc(userDocId, { 30 | success : function(userDoc) { 31 | var profile = userDoc["couch.app.profile"]; 32 | if (profile) { 33 | profile.name = userDoc.name; 34 | profileReady(profile); 35 | } else { 36 | newProfile(userCtx) 37 | } 38 | } 39 | }); 40 | 41 | function profileReady(profile) { 42 | widget.html($.mustache(templates.profileReady, profile)); 43 | if (opts.profileReady) {opts.profileReady(profile)}; 44 | }; 45 | 46 | function storeProfileOnUserDoc(newProfile) { 47 | // store the user profile on the user account document 48 | $.couch.userDb(function(db) { 49 | var userDocId = "org.couchdb.user:"+userCtx.name; 50 | db.openDoc(userDocId, { 51 | success : function(userDoc) { 52 | userDoc["couch.app.profile"] = newProfile; 53 | db.saveDoc(userDoc, { 54 | success : function() { 55 | newProfile.name = userDoc.name; 56 | profileReady(newProfile); 57 | } 58 | }); 59 | } 60 | }); 61 | }); 62 | }; 63 | 64 | function newProfile(userCtx) { 65 | widget.html($.mustache(templates.newProfile, userCtx)); 66 | widget.find("form").submit(function(e) { 67 | e.preventDefault(); 68 | var form = this; 69 | var name = $("input[name=userCtxName]",form).val(); 70 | var newProfile = { 71 | rand : Math.random().toString(), 72 | nickname : $("input[name=nickname]",form).val(), 73 | email : $("input[name=email]",form).val(), 74 | url : $("input[name=url]",form).val() 75 | }; 76 | // setup gravatar_url if md5.js is loaded 77 | if (hex_md5) { 78 | newProfile.gravatar_url = 'http://www.gravatar.com/avatar/'+hex_md5(newProfile.email || newProfile.rand)+'.jpg?s=40&d=identicon'; 79 | } 80 | storeProfileOnUserDoc(newProfile); 81 | return false; 82 | }); 83 | }; 84 | } 85 | })(jQuery); 86 | -------------------------------------------------------------------------------- /vendor/couchapp/_attachments/jquery.couchLogin.js: -------------------------------------------------------------------------------- 1 | // Copyright Chris Anderson 2011 2 | // Apache 2.0 License 3 | // jquery.couchLogin.js 4 | // 5 | // Example Usage (loggedIn and loggedOut callbacks are optional): 6 | // $("#mylogindiv").couchLogin({ 7 | // loggedIn : function(userCtx) { 8 | // alert("hello "+userCtx.name); 9 | // }, 10 | // loggedOut : function() { 11 | // alert("bye bye"); 12 | // } 13 | // }); 14 | 15 | (function($) { 16 | $.fn.couchLogin = function(opts) { 17 | var elem = $(this); 18 | opts = opts || {}; 19 | function initWidget() { 20 | $.couch.session({ 21 | success : function(session) { 22 | var userCtx = session.userCtx; 23 | if (userCtx.name) { 24 | elem.empty(); 25 | elem.append(loggedIn(session)); 26 | if (opts.loggedIn) {opts.loggedIn(session)} 27 | } else if (userCtx.roles.indexOf("_admin") != -1) { 28 | elem.html(templates.adminParty); 29 | } else { 30 | elem.html(templates.loggedOut); 31 | if (opts.loggedOut) {opts.loggedOut()} 32 | }; 33 | } 34 | }); 35 | }; 36 | initWidget(); 37 | function doLogin(name, pass) { 38 | $.couch.login({name:name, password:pass, success:initWidget}); 39 | }; 40 | elem.delegate("a[href=#signup]", "click", function() { 41 | elem.html(templates.signupForm); 42 | elem.find('input[name="name"]').focus(); 43 | return false; 44 | }); 45 | elem.delegate("a[href=#login]", "click", function() { 46 | elem.html(templates.loginForm); 47 | elem.find('input[name="name"]').focus(); 48 | return false; 49 | }); 50 | elem.delegate("a[href=#logout]", "click", function() { 51 | $.couch.logout({success : initWidget}); 52 | return false; 53 | }); 54 | elem.delegate("form.login", "submit", function() { 55 | doLogin($('input[name=name]', this).val(), 56 | $('input[name=password]', this).val()); 57 | return false; 58 | }); 59 | elem.delegate("form.signup", "submit", function() { 60 | var name = $('input[name=name]', this).val(), 61 | pass = $('input[name=password]', this).val(); 62 | $.couch.signup({name : name}, pass, { 63 | success : function() {doLogin(name, pass)} 64 | }); 65 | return false; 66 | }); 67 | } 68 | var templates = { 69 | adminParty : 'Admin party, everyone is admin! Fix this in Futon before proceeding.
', 70 | loggedOut : 'Signup or Login', 71 | loginForm : '', 72 | signupForm : '' 73 | }; 74 | function loggedIn(r) { 75 | var auth_db = encodeURIComponent(r.info.authentication_db) 76 | , uri_name = encodeURIComponent(r.userCtx.name) 77 | , span = $('Welcome ! Logout?'); 80 | $('a.name', span).text(r.userCtx.name); // you can get the user name here 81 | return span; 82 | } 83 | })(jQuery); 84 | -------------------------------------------------------------------------------- /_attachments/script/sha1.js: -------------------------------------------------------------------------------- 1 | /* 2 | * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined 3 | * in FIPS PUB 180-1 4 | * Version 2.1a Copyright Paul Johnston 2000 - 2002. 5 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 6 | * Distributed under the BSD License 7 | * See http://pajhome.org.uk/crypt/md5 for details. 8 | */ 9 | 10 | /* 11 | * Configurable variables. You may need to tweak these to be compatible with 12 | * the server-side, but the defaults work in most cases. 13 | */ 14 | var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ 15 | var b64pad = "="; /* base-64 pad character. "=" for strict RFC compliance */ 16 | var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ 17 | 18 | /* 19 | * These are the functions you'll usually want to call 20 | * They take string arguments and return either hex or base-64 encoded strings 21 | */ 22 | function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} 23 | function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} 24 | function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} 25 | function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} 26 | function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} 27 | function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} 28 | 29 | /* 30 | * Perform a simple self-test to see if the VM is working 31 | */ 32 | function sha1_vm_test() 33 | { 34 | return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; 35 | } 36 | 37 | /* 38 | * Calculate the SHA-1 of an array of big-endian words, and a bit length 39 | */ 40 | function core_sha1(x, len) 41 | { 42 | /* append padding */ 43 | x[len >> 5] |= 0x80 << (24 - len % 32); 44 | x[((len + 64 >> 9) << 4) + 15] = len; 45 | 46 | var w = Array(80); 47 | var a = 1732584193; 48 | var b = -271733879; 49 | var c = -1732584194; 50 | var d = 271733878; 51 | var e = -1009589776; 52 | 53 | for(var i = 0; i < x.length; i += 16) 54 | { 55 | var olda = a; 56 | var oldb = b; 57 | var oldc = c; 58 | var oldd = d; 59 | var olde = e; 60 | 61 | for(var j = 0; j < 80; j++) 62 | { 63 | if(j < 16) w[j] = x[i + j]; 64 | else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); 65 | var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), 66 | safe_add(safe_add(e, w[j]), sha1_kt(j))); 67 | e = d; 68 | d = c; 69 | c = rol(b, 30); 70 | b = a; 71 | a = t; 72 | } 73 | 74 | a = safe_add(a, olda); 75 | b = safe_add(b, oldb); 76 | c = safe_add(c, oldc); 77 | d = safe_add(d, oldd); 78 | e = safe_add(e, olde); 79 | } 80 | return Array(a, b, c, d, e); 81 | 82 | } 83 | 84 | /* 85 | * Perform the appropriate triplet combination function for the current 86 | * iteration 87 | */ 88 | function sha1_ft(t, b, c, d) 89 | { 90 | if(t < 20) return (b & c) | ((~b) & d); 91 | if(t < 40) return b ^ c ^ d; 92 | if(t < 60) return (b & c) | (b & d) | (c & d); 93 | return b ^ c ^ d; 94 | } 95 | 96 | /* 97 | * Determine the appropriate additive constant for the current iteration 98 | */ 99 | function sha1_kt(t) 100 | { 101 | return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : 102 | (t < 60) ? -1894007588 : -899497514; 103 | } 104 | 105 | /* 106 | * Calculate the HMAC-SHA1 of a key and some data 107 | */ 108 | function core_hmac_sha1(key, data) 109 | { 110 | var bkey = str2binb(key); 111 | if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); 112 | 113 | var ipad = Array(16), opad = Array(16); 114 | for(var i = 0; i < 16; i++) 115 | { 116 | ipad[i] = bkey[i] ^ 0x36363636; 117 | opad[i] = bkey[i] ^ 0x5C5C5C5C; 118 | } 119 | 120 | var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); 121 | return core_sha1(opad.concat(hash), 512 + 160); 122 | } 123 | 124 | /* 125 | * Add integers, wrapping at 2^32. This uses 16-bit operations internally 126 | * to work around bugs in some JS interpreters. 127 | */ 128 | function safe_add(x, y) 129 | { 130 | var lsw = (x & 0xFFFF) + (y & 0xFFFF); 131 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16); 132 | return (msw << 16) | (lsw & 0xFFFF); 133 | } 134 | 135 | /* 136 | * Bitwise rotate a 32-bit number to the left. 137 | */ 138 | function rol(num, cnt) 139 | { 140 | return (num << cnt) | (num >>> (32 - cnt)); 141 | } 142 | 143 | /* 144 | * Convert an 8-bit or 16-bit string to an array of big-endian words 145 | * In 8-bit function, characters >255 have their hi-byte silently ignored. 146 | */ 147 | function str2binb(str) 148 | { 149 | var bin = Array(); 150 | var mask = (1 << chrsz) - 1; 151 | for(var i = 0; i < str.length * chrsz; i += chrsz) 152 | bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32); 153 | return bin; 154 | } 155 | 156 | /* 157 | * Convert an array of big-endian words to a string 158 | */ 159 | function binb2str(bin) 160 | { 161 | var str = ""; 162 | var mask = (1 << chrsz) - 1; 163 | for(var i = 0; i < bin.length * 32; i += chrsz) 164 | str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); 165 | return str; 166 | } 167 | 168 | /* 169 | * Convert an array of big-endian words to a hex string. 170 | */ 171 | function binb2hex(binarray) 172 | { 173 | var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; 174 | var str = ""; 175 | for(var i = 0; i < binarray.length * 4; i++) 176 | { 177 | str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + 178 | hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); 179 | } 180 | return str; 181 | } 182 | 183 | /* 184 | * Convert an array of big-endian words to a base-64 string 185 | */ 186 | function binb2b64(binarray) 187 | { 188 | var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 189 | var str = ""; 190 | for(var i = 0; i < binarray.length * 4; i += 3) 191 | { 192 | var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) 193 | | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) 194 | | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); 195 | for(var j = 0; j < 4; j++) 196 | { 197 | if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; 198 | else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); 199 | } 200 | } 201 | return str; 202 | } 203 | -------------------------------------------------------------------------------- /vendor/couchapp/_attachments/jquery.couch.app.js: -------------------------------------------------------------------------------- 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 3 | // of 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 | // Usage: The passed in function is called when the page is ready. 14 | // CouchApp passes in the app object, which takes care of linking to 15 | // the proper database, and provides access to the CouchApp helpers. 16 | // $.couch.app(function(app) { 17 | // app.db.view(...) 18 | // ... 19 | // }); 20 | 21 | (function($) { 22 | 23 | function Design(db, name, code) { 24 | this.doc_id = "_design/"+name; 25 | if (code) { 26 | this.code_path = this.doc_id + "/" + code; 27 | } else { 28 | this.code_path = this.doc_id; 29 | } 30 | this.view = function(view, opts) { 31 | db.view(name+'/'+view, opts); 32 | }; 33 | this.list = function(list, view, opts) { 34 | db.list(name+'/'+list, view, opts); 35 | }; 36 | } 37 | 38 | function docForm() { alert("docForm has been moved to vendor/couchapp/lib/docForm.js, use app.require to load") }; 39 | 40 | function resolveModule(path, names, parents, current) { 41 | parents = parents || []; 42 | if (names.length === 0) { 43 | if (typeof current != "string") { 44 | throw ["error","invalid_require_path", 45 | 'Must require a JavaScript string, not: '+(typeof current)]; 46 | } 47 | return [current, parents]; 48 | } 49 | var n = names.shift(); 50 | if (n == '..') { 51 | parents.pop(); 52 | var pp = parents.pop(); 53 | if (!pp) { 54 | throw ["error", "invalid_require_path", path]; 55 | } 56 | return resolveModule(path, names, parents, pp); 57 | } else if (n == '.') { 58 | var p = parents.pop(); 59 | if (!p) { 60 | throw ["error", "invalid_require_path", path]; 61 | } 62 | return resolveModule(path, names, parents, p); 63 | } else { 64 | parents = []; 65 | } 66 | if (!current[n]) { 67 | throw ["error", "invalid_require_path", path]; 68 | } 69 | parents.push(current); 70 | return resolveModule(path, names, parents, current[n]); 71 | } 72 | 73 | function makeRequire(ddoc) { 74 | var moduleCache = []; 75 | function getCachedModule(name, parents) { 76 | var key, i, len = moduleCache.length; 77 | for (i=0;ioptions object which defines a success callback which
18 | * is called with the data returned from the http request to CouchDB, you can
19 | * find the other settings that can be used in the options object
20 | * from
21 | * jQuery.ajax settings
22 | * $.couch.activeTasks({
23 | * success: function (data) {
24 | * console.log(data);
25 | * }
26 | * });
27 | * Outputs (for example):
28 | * [
29 | * {
30 | * "pid" : "<0.11599.0>",
31 | * "status" : "Copied 0 of 18369 changes (0%)",
32 | * "task" : "recipes",
33 | * "type" : "Database Compaction"
34 | * }
35 | *]
36 | */
37 | (function($) {
38 |
39 | $.couch = $.couch || {};
40 | /** @lends $.couch */
41 |
42 | /**
43 | * @private
44 | */
45 | function encodeDocId(docID) {
46 | var parts = docID.split("/");
47 | if (parts[0] == "_design") {
48 | parts.shift();
49 | return "_design/" + encodeURIComponent(parts.join('/'));
50 | }
51 | return encodeURIComponent(docID);
52 | }
53 |
54 | /**
55 | * @private
56 | */
57 |
58 | var uuidCache = [];
59 |
60 | $.extend($.couch, {
61 | urlPrefix: '',
62 |
63 | /**
64 | * You can obtain a list of active tasks by using the /_active_tasks URL.
65 | * The result is a JSON array of the currently running tasks, with each task
66 | * being described with a single object.
67 | * @see docs for /_active_tasks
70 | * @param {ajaxSettings} options jQuery ajax settings
72 | */
73 | activeTasks: function(options) {
74 | ajax(
75 | {url: this.urlPrefix + "/_active_tasks"},
76 | options,
77 | "Active task status could not be retrieved"
78 | );
79 | },
80 |
81 | /**
82 | * Returns a list of all the databases in the CouchDB instance
83 | * @see docs for /_all_dbs
86 | * @param {ajaxSettings} options jQuery ajax settings
88 | */
89 | allDbs: function(options) {
90 | ajax(
91 | {url: this.urlPrefix + "/_all_dbs"},
92 | options,
93 | "An error occurred retrieving the list of all databases"
94 | );
95 | },
96 |
97 | /**
98 | * View and edit the CouchDB configuration, called with just the options
99 | * parameter the entire config is returned, you can be more specific by
100 | * passing the section and option parameters, if you specify a value that
101 | * value will be stored in the configuration.
102 | * @see docs for /_config
105 | * @param {ajaxSettings} options
106 | *
107 | * jQuery ajax settings
108 | * @param {String} [section] the section of the config
109 | * @param {String} [option] the particular config option
110 | * @param {String} [value] value to be set
111 | */
112 | config: function(options, section, option, value) {
113 | var req = {url: this.urlPrefix + "/_config/"};
114 | if (section) {
115 | req.url += encodeURIComponent(section) + "/";
116 | if (option) {
117 | req.url += encodeURIComponent(option);
118 | }
119 | }
120 | if (value === null) {
121 | req.type = "DELETE";
122 | } else if (value !== undefined) {
123 | req.type = "PUT";
124 | req.data = toJSON(value);
125 | req.contentType = "application/json";
126 | req.processData = false
127 | }
128 |
129 | ajax(req, options,
130 | "An error occurred retrieving/updating the server configuration"
131 | );
132 | },
133 |
134 | /**
135 | * Returns the session information for the currently logged in user.
136 | * @param {ajaxSettings} options
137 | *
138 | * jQuery ajax settings
139 | */
140 | session: function(options) {
141 | options = options || {};
142 | $.ajax({
143 | type: "GET", url: this.urlPrefix + "/_session",
144 | beforeSend: function(xhr) {
145 | xhr.setRequestHeader('Accept', 'application/json');
146 | },
147 | complete: function(req) {
148 | var resp = $.parseJSON(req.responseText);
149 | if (req.status == 200) {
150 | if (options.success) options.success(resp);
151 | } else if (options.error) {
152 | options.error(req.status, resp.error, resp.reason);
153 | } else {
154 | alert("An error occurred getting session info: " + resp.reason);
155 | }
156 | }
157 | });
158 | },
159 |
160 | /**
161 | * @private
162 | */
163 | userDb : function(callback) {
164 | $.couch.session({
165 | success : function(resp) {
166 | var userDb = $.couch.db(resp.info.authentication_db);
167 | callback(userDb);
168 | }
169 | });
170 | },
171 |
172 | /**
173 | * Create a new user on the CouchDB server, user_doc is an
174 | * object with a name field and other information you want
175 | * to store relating to that user, for example
176 | * {"name": "daleharvey"}
177 | * @param {Object} user_doc Users details
178 | * @param {String} password Users password
179 | * @param {ajaxSettings} options
180 | *
181 | * jQuery ajax settings
182 | */
183 | signup: function(user_doc, password, options) {
184 | options = options || {};
185 | // prepare user doc based on name and password
186 | user_doc = this.prepareUserDoc(user_doc, password);
187 | $.couch.userDb(function(db) {
188 | db.saveDoc(user_doc, options);
189 | });
190 | },
191 |
192 | /**
193 | * Populates a user doc with a new password.
194 | * @param {Object} user_doc User details
195 | * @param {String} new_password New Password
196 | */
197 | prepareUserDoc: function(user_doc, new_password) {
198 | if (typeof hex_sha1 == "undefined") {
199 | alert("creating a user doc requires sha1.js to be loaded in the page");
200 | return;
201 | }
202 | var user_prefix = "org.couchdb.user:";
203 | user_doc._id = user_doc._id || user_prefix + user_doc.name;
204 | if (new_password) {
205 | // handle the password crypto
206 | user_doc.salt = $.couch.newUUID();
207 | user_doc.password_sha = hex_sha1(new_password + user_doc.salt);
208 | }
209 | user_doc.type = "user";
210 | if (!user_doc.roles) {
211 | user_doc.roles = [];
212 | }
213 | return user_doc;
214 | },
215 |
216 | /**
217 | * Authenticate against CouchDB, the options parameter is
218 | *expected to have name and password fields.
219 | * @param {ajaxSettings} options
220 | *
221 | * jQuery ajax settings
222 | */
223 | login: function(options) {
224 | options = options || {};
225 | $.ajax({
226 | type: "POST", url: this.urlPrefix + "/_session", dataType: "json",
227 | data: {name: options.name, password: options.password},
228 | beforeSend: function(xhr) {
229 | xhr.setRequestHeader('Accept', 'application/json');
230 | },
231 | complete: function(req) {
232 | var resp = $.parseJSON(req.responseText);
233 | if (req.status == 200) {
234 | if (options.success) options.success(resp);
235 | } else if (options.error) {
236 | options.error(req.status, resp.error, resp.reason);
237 | } else {
238 | alert("An error occurred logging in: " + resp.reason);
239 | }
240 | }
241 | });
242 | },
243 |
244 |
245 | /**
246 | * Delete your current CouchDB user session
247 | * @param {ajaxSettings} options
248 | *
249 | * jQuery ajax settings
250 | */
251 | logout: function(options) {
252 | options = options || {};
253 | $.ajax({
254 | type: "DELETE", url: this.urlPrefix + "/_session", dataType: "json",
255 | username : "_", password : "_",
256 | beforeSend: function(xhr) {
257 | xhr.setRequestHeader('Accept', 'application/json');
258 | },
259 | complete: function(req) {
260 | var resp = $.parseJSON(req.responseText);
261 | if (req.status == 200) {
262 | if (options.success) options.success(resp);
263 | } else if (options.error) {
264 | options.error(req.status, resp.error, resp.reason);
265 | } else {
266 | alert("An error occurred logging out: " + resp.reason);
267 | }
268 | }
269 | });
270 | },
271 |
272 | /**
273 | * @namespace
274 | * $.couch.db is used to communicate with a specific CouchDB database
275 | * var $db = $.couch.db("mydatabase");
276 | *$db.allApps({
277 | * success: function (data) {
278 | * ... process data ...
279 | * }
280 | *});
281 | *
282 | */
283 | db: function(name, db_opts) {
284 | db_opts = db_opts || {};
285 | var rawDocs = {};
286 | function maybeApplyVersion(doc) {
287 | if (doc._id && doc._rev && rawDocs[doc._id] &&
288 | rawDocs[doc._id].rev == doc._rev) {
289 | // todo: can we use commonjs require here?
290 | if (typeof Base64 == "undefined") {
291 | alert("please include /_utils/script/base64.js in the page for " +
292 | "base64 support");
293 | return false;
294 | } else {
295 | doc._attachments = doc._attachments || {};
296 | doc._attachments["rev-"+doc._rev.split("-")[0]] = {
297 | content_type :"application/json",
298 | data : Base64.encode(rawDocs[doc._id].raw)
299 | };
300 | return true;
301 | }
302 | }
303 | };
304 | return /** @lends $.couch.db */{
305 | name: name,
306 | uri: this.urlPrefix + "/" + encodeURIComponent(name) + "/",
307 |
308 | /**
309 | * Request compaction of the specified database.
310 | * @see docs for /db/_compact
313 | * @param {ajaxSettings} options
314 | *
315 | * jQuery ajax settings
316 | */
317 | compact: function(options) {
318 | $.extend(options, {successStatus: 202});
319 | ajax({
320 | type: "POST", url: this.uri + "_compact",
321 | data: "", processData: false
322 | },
323 | options,
324 | "The database could not be compacted"
325 | );
326 | },
327 |
328 | /**
329 | * Cleans up the cached view output on disk for a given view.
330 | * @see docs for /db/_compact
333 | * @param {ajaxSettings} options jQuery ajax settings
335 | */
336 | viewCleanup: function(options) {
337 | $.extend(options, {successStatus: 202});
338 | ajax({
339 | type: "POST", url: this.uri + "_view_cleanup",
340 | data: "", processData: false
341 | },
342 | options,
343 | "The views could not be cleaned up"
344 | );
345 | },
346 |
347 | /**
348 | * Compacts the view indexes associated with the specified design
349 | * document. You can use this in place of the full database compaction
350 | * if you know a specific set of view indexes have been affected by a
351 | * recent database change.
352 | * @see docs for /db/_compact/design-doc
355 | * @param {String} groupname Name of design-doc to compact
356 | * @param {ajaxSettings} options jQuery ajax settings
358 | */
359 | compactView: function(groupname, options) {
360 | $.extend(options, {successStatus: 202});
361 | ajax({
362 | type: "POST", url: this.uri + "_compact/" + groupname,
363 | data: "", processData: false
364 | },
365 | options,
366 | "The view could not be compacted"
367 | );
368 | },
369 |
370 | /**
371 | * Create a new database
372 | * @see docs for PUT /db/
375 | * @param {ajaxSettings} options jQuery ajax settings
377 | */
378 | create: function(options) {
379 | $.extend(options, {successStatus: 201});
380 | ajax({
381 | type: "PUT", url: this.uri, contentType: "application/json",
382 | data: "", processData: false
383 | },
384 | options,
385 | "The database could not be created"
386 | );
387 | },
388 |
389 | /**
390 | * Deletes the specified database, and all the documents and
391 | * attachments contained within it.
392 | * @see docs for DELETE /db/
395 | * @param {ajaxSettings} options jQuery ajax settings
397 | */
398 | drop: function(options) {
399 | ajax(
400 | {type: "DELETE", url: this.uri},
401 | options,
402 | "The database could not be deleted"
403 | );
404 | },
405 |
406 | /**
407 | * Gets information about the specified database.
408 | * @see docs for GET /db/
411 | * @param {ajaxSettings} options jQuery ajax settings
413 | */
414 | info: function(options) {
415 | ajax(
416 | {url: this.uri},
417 | options,
418 | "Database information could not be retrieved"
419 | );
420 | },
421 |
422 | /**
423 | * @namespace
424 | * $.couch.db.changes provides an API for subscribing to the changes
425 | * feed
426 | * var $changes = $.couch.db("mydatabase").changes();
427 | *$changes.onChange = function (data) {
428 | * ... process data ...
429 | * }
430 | * $changes.stop();
431 | *
432 | */
433 | changes: function(since, options) {
434 |
435 | options = options || {};
436 | // set up the promise object within a closure for this handler
437 | var timeout = 100, db = this, active = true,
438 | listeners = [],
439 | promise = /** @lends $.couch.db.changes */ {
440 | /**
441 | * Add a listener callback
442 | * @see docs for /db/_changes
445 | * @param {Function} fun Callback function to run when
446 | * notified of changes.
447 | */
448 | onChange : function(fun) {
449 | listeners.push(fun);
450 | },
451 | /**
452 | * Stop subscribing to the changes feed
453 | */
454 | stop : function() {
455 | active = false;
456 | }
457 | };
458 | // call each listener when there is a change
459 | function triggerListeners(resp) {
460 | $.each(listeners, function() {
461 | this(resp);
462 | });
463 | };
464 | // when there is a change, call any listeners, then check for
465 | // another change
466 | options.success = function(resp) {
467 | timeout = 100;
468 | if (active) {
469 | since = resp.last_seq;
470 | triggerListeners(resp);
471 | getChangesSince();
472 | };
473 | };
474 | options.error = function() {
475 | if (active) {
476 | setTimeout(getChangesSince, timeout);
477 | timeout = timeout * 2;
478 | }
479 | };
480 | // actually make the changes request
481 | function getChangesSince() {
482 | var opts = $.extend({heartbeat : 10 * 1000}, options, {
483 | feed : "longpoll",
484 | since : since
485 | });
486 | ajax(
487 | {url: db.uri + "_changes"+encodeOptions(opts)},
488 | options,
489 | "Error connecting to "+db.uri+"/_changes."
490 | );
491 | }
492 | // start the first request
493 | if (since) {
494 | getChangesSince();
495 | } else {
496 | db.info({
497 | success : function(info) {
498 | since = info.update_seq;
499 | getChangesSince();
500 | }
501 | });
502 | }
503 | return promise;
504 | },
505 |
506 | /**
507 | * Fetch all the docs in this db, you can specify an array of keys to
508 | * fetch by passing the keys field in the
509 | * options
510 | * parameter.
511 | * @see docs for /db/all_docs/
514 | * @param {ajaxSettings} options jQuery ajax settings
516 | */
517 | allDocs: function(options) {
518 | var type = "GET";
519 | var data = null;
520 | if (options["keys"]) {
521 | type = "POST";
522 | var keys = options["keys"];
523 | delete options["keys"];
524 | data = toJSON({ "keys": keys });
525 | }
526 | ajax({
527 | type: type,
528 | data: data,
529 | url: this.uri + "_all_docs" + encodeOptions(options)
530 | },
531 | options,
532 | "An error occurred retrieving a list of all documents"
533 | );
534 | },
535 |
536 | /**
537 | * Fetch all the design docs in this db
538 | * @param {ajaxSettings} options jQuery ajax settings
540 | */
541 | allDesignDocs: function(options) {
542 | this.allDocs($.extend(
543 | {startkey:"_design", endkey:"_design0"}, options));
544 | },
545 |
546 | /**
547 | * Fetch all the design docs with an index.html, options
548 | * parameter expects an eachApp field which is a callback
549 | * called on each app found.
550 | * @param {ajaxSettings} options jQuery ajax settings
552 | */
553 | allApps: function(options) {
554 | options = options || {};
555 | var self = this;
556 | if (options.eachApp) {
557 | this.allDesignDocs({
558 | success: function(resp) {
559 | $.each(resp.rows, function() {
560 | self.openDoc(this.id, {
561 | success: function(ddoc) {
562 | var index, appPath, appName = ddoc._id.split('/');
563 | appName.shift();
564 | appName = appName.join('/');
565 | index = ddoc.couchapp && ddoc.couchapp.index;
566 | if (index) {
567 | appPath = ['', name, ddoc._id, index].join('/');
568 | } else if (ddoc._attachments &&
569 | ddoc._attachments["index.html"]) {
570 | appPath = ['', name, ddoc._id, "index.html"].join('/');
571 | }
572 | if (appPath) options.eachApp(appName, appPath, ddoc);
573 | }
574 | });
575 | });
576 | }
577 | });
578 | } else {
579 | alert("Please provide an eachApp function for allApps()");
580 | }
581 | },
582 |
583 | /**
584 | * Returns the specified doc from the specified db.
585 | * @see docs for GET /db/doc
588 | * @param {String} docId id of document to fetch
589 | * @param {ajaxSettings} options jQuery ajax settings
591 | * @param {ajaxSettings} ajaxOptions jQuery ajax settings
593 | */
594 | openDoc: function(docId, options, ajaxOptions) {
595 | options = options || {};
596 | if (db_opts.attachPrevRev || options.attachPrevRev) {
597 | $.extend(options, {
598 | beforeSuccess : function(req, doc) {
599 | rawDocs[doc._id] = {
600 | rev : doc._rev,
601 | raw : req.responseText
602 | };
603 | }
604 | });
605 | } else {
606 | $.extend(options, {
607 | beforeSuccess : function(req, doc) {
608 | if (doc["jquery.couch.attachPrevRev"]) {
609 | rawDocs[doc._id] = {
610 | rev : doc._rev,
611 | raw : req.responseText
612 | };
613 | }
614 | }
615 | });
616 | }
617 | ajax({url: this.uri + encodeDocId(docId) + encodeOptions(options)},
618 | options,
619 | "The document could not be retrieved",
620 | ajaxOptions
621 | );
622 | },
623 |
624 | /**
625 | * Create a new document in the specified database, using the supplied
626 | * JSON document structure. If the JSON structure includes the _id
627 | * field, then the document will be created with the specified document
628 | * ID. If the _id field is not specified, a new unique ID will be
629 | * generated.
630 | * @see docs for GET /db/doc
633 | * @param {String} doc document to save
634 | * @param {ajaxSettings} options jQuery ajax settings
636 | */
637 | saveDoc: function(doc, options) {
638 | options = options || {};
639 | var db = this;
640 | var beforeSend = fullCommit(options);
641 | if (doc._id === undefined) {
642 | var method = "POST";
643 | var uri = this.uri;
644 | } else {
645 | var method = "PUT";
646 | var uri = this.uri + encodeDocId(doc._id);
647 | }
648 | var versioned = maybeApplyVersion(doc);
649 | $.ajax({
650 | type: method, url: uri + encodeOptions(options),
651 | contentType: "application/json",
652 | dataType: "json", data: toJSON(doc),
653 | beforeSend : beforeSend,
654 | complete: function(req) {
655 | var resp = $.parseJSON(req.responseText);
656 | if (req.status == 200 || req.status == 201 || req.status == 202) {
657 | doc._id = resp.id;
658 | doc._rev = resp.rev;
659 | if (versioned) {
660 | db.openDoc(doc._id, {
661 | attachPrevRev : true,
662 | success : function(d) {
663 | doc._attachments = d._attachments;
664 | if (options.success) options.success(resp);
665 | }
666 | });
667 | } else {
668 | if (options.success) options.success(resp);
669 | }
670 | } else if (options.error) {
671 | options.error(req.status, resp.error, resp.reason);
672 | } else {
673 | alert("The document could not be saved: " + resp.reason);
674 | }
675 | }
676 | });
677 | },
678 |
679 | /**
680 | * Save a list of documents
681 | * @see docs for /db/_bulk_docs
684 | * @param {Object[]} docs List of documents to save
685 | * @param {ajaxSettings} options jQuery ajax settings
687 | */
688 | bulkSave: function(docs, options) {
689 | var beforeSend = fullCommit(options);
690 | $.extend(options, {successStatus: 201, beforeSend : beforeSend});
691 | ajax({
692 | type: "POST",
693 | url: this.uri + "_bulk_docs" + encodeOptions(options),
694 | contentType: "application/json", data: toJSON(docs)
695 | },
696 | options,
697 | "The documents could not be saved"
698 | );
699 | },
700 |
701 | /**
702 | * Deletes the specified document from the database. You must supply
703 | * the current (latest) revision and id of the document
704 | * to delete eg removeDoc({_id:"mydoc", _rev: "1-2345"})
705 | * @see docs for DELETE /db/doc
708 | * @param {Object} doc Document to delete
709 | * @param {ajaxSettings} options jQuery ajax settings
711 | */
712 | removeDoc: function(doc, options) {
713 | ajax({
714 | type: "DELETE",
715 | url: this.uri +
716 | encodeDocId(doc._id) +
717 | encodeOptions({rev: doc._rev})
718 | },
719 | options,
720 | "The document could not be deleted"
721 | );
722 | },
723 |
724 | /**
725 | * Remove a set of documents
726 | * @see docs for /db/_bulk_docs
729 | * @param {String[]} docs List of document id's to remove
730 | * @param {ajaxSettings} options jQuery ajax settings
732 | */
733 | bulkRemove: function(docs, options){
734 | docs.docs = $.each(
735 | docs.docs, function(i, doc){
736 | doc._deleted = true;
737 | }
738 | );
739 | $.extend(options, {successStatus: 201});
740 | ajax({
741 | type: "POST",
742 | url: this.uri + "_bulk_docs" + encodeOptions(options),
743 | data: toJSON(docs)
744 | },
745 | options,
746 | "The documents could not be deleted"
747 | );
748 | },
749 |
750 | /**
751 | * The COPY command (which is non-standard HTTP) copies an existing
752 | * document to a new or existing document.
753 | * @see docs for COPY /db/doc
756 | * @param {String[]} docId document id to copy
757 | * @param {ajaxSettings} options jQuery ajax settings
759 | * @param {ajaxSettings} options jQuery ajax settings
761 | */
762 | copyDoc: function(docId, options, ajaxOptions) {
763 | ajaxOptions = $.extend(ajaxOptions, {
764 | complete: function(req) {
765 | var resp = $.parseJSON(req.responseText);
766 | if (req.status == 201) {
767 | if (options.success) options.success(resp);
768 | } else if (options.error) {
769 | options.error(req.status, resp.error, resp.reason);
770 | } else {
771 | alert("The document could not be copied: " + resp.reason);
772 | }
773 | }
774 | });
775 | ajax({
776 | type: "COPY",
777 | url: this.uri + encodeDocId(docId)
778 | },
779 | options,
780 | "The document could not be copied",
781 | ajaxOptions
782 | );
783 | },
784 |
785 | /**
786 | * Creates (and executes) a temporary view based on the view function
787 | * supplied in the JSON request.
788 | * @see docs for /db/_temp_view
791 | * @param {Function} mapFun Map function
792 | * @param {Function} reduceFun Reduce function
793 | * @param {Function} language Language the map / reduce funs are
794 | * implemented in
795 | * @param {ajaxSettings} options jQuery ajax settings
797 | */
798 | query: function(mapFun, reduceFun, language, options) {
799 | language = language || "javascript";
800 | if (typeof(mapFun) !== "string") {
801 | mapFun = mapFun.toSource ? mapFun.toSource()
802 | : "(" + mapFun.toString() + ")";
803 | }
804 | var body = {language: language, map: mapFun};
805 | if (reduceFun != null) {
806 | if (typeof(reduceFun) !== "string")
807 | reduceFun = reduceFun.toSource ? reduceFun.toSource()
808 | : "(" + reduceFun.toString() + ")";
809 | body.reduce = reduceFun;
810 | }
811 | ajax({
812 | type: "POST",
813 | url: this.uri + "_temp_view" + encodeOptions(options),
814 | contentType: "application/json", data: toJSON(body)
815 | },
816 | options,
817 | "An error occurred querying the database"
818 | );
819 | },
820 |
821 | /**
822 | * Fetch a _list view output, you can specify a list of
823 | * keys in the options object to recieve only those keys.
824 | * @see
827 | * docs for /db/_design/design-doc/_list/l1/v1
828 | * @param {String} list Listname in the form of ddoc/listname
829 | * @param {String} view View to run list against
830 | * @param {options} CouchDB View Options
832 | * @param {ajaxSettings} options jQuery ajax settings
834 | */
835 | list: function(list, view, options, ajaxOptions) {
836 | var list = list.split('/');
837 | var options = options || {};
838 | var type = 'GET';
839 | var data = null;
840 | if (options['keys']) {
841 | type = 'POST';
842 | var keys = options['keys'];
843 | delete options['keys'];
844 | data = toJSON({'keys': keys });
845 | }
846 | ajax({
847 | type: type,
848 | data: data,
849 | url: this.uri + '_design/' + list[0] +
850 | '/_list/' + list[1] + '/' + view + encodeOptions(options)
851 | },
852 | ajaxOptions, 'An error occured accessing the list'
853 | );
854 | },
855 |
856 | /**
857 | * Executes the specified view-name from the specified design-doc
858 | * design document, you can specify a list of keys
859 | * in the options object to recieve only those keys.
860 | * @see docs for /db/
863 | * _design/design-doc/_list/l1/v1
864 | * @param {String} name View to run list against
865 | * @param {ajaxSettings} options jQuery ajax settings
867 | */
868 | view: function(name, options) {
869 | var name = name.split('/');
870 | var options = options || {};
871 | var type = "GET";
872 | var data= null;
873 | if (options["keys"]) {
874 | type = "POST";
875 | var keys = options["keys"];
876 | delete options["keys"];
877 | data = toJSON({ "keys": keys });
878 | }
879 | ajax({
880 | type: type,
881 | data: data,
882 | url: this.uri + "_design/" + name[0] +
883 | "/_view/" + name[1] + encodeOptions(options)
884 | },
885 | options, "An error occurred accessing the view"
886 | );
887 | },
888 |
889 | /**
890 | * Fetch an arbitrary CouchDB database property
891 | * @see docs for /db/_prop
893 | * @param {String} propName Propery name to fetch
894 | * @param {ajaxSettings} options jQuery ajax settings
896 | * @param {ajaxSettings} ajaxOptions jQuery ajax settings
898 | */
899 | getDbProperty: function(propName, options, ajaxOptions) {
900 | ajax({url: this.uri + propName + encodeOptions(options)},
901 | options,
902 | "The property could not be retrieved",
903 | ajaxOptions
904 | );
905 | },
906 |
907 | /**
908 | * Set an arbitrary CouchDB database property
909 | * @see docs for /db/_prop
911 | * @param {String} propName Propery name to fetch
912 | * @param {String} propValue Propery value to set
913 | * @param {ajaxSettings} options jQuery ajax settings
915 | * @param {ajaxSettings} ajaxOptions jQuery ajax settings
917 | */
918 | setDbProperty: function(propName, propValue, options, ajaxOptions) {
919 | ajax({
920 | type: "PUT",
921 | url: this.uri + propName + encodeOptions(options),
922 | data : JSON.stringify(propValue)
923 | },
924 | options,
925 | "The property could not be updated",
926 | ajaxOptions
927 | );
928 | }
929 | };
930 | },
931 |
932 | encodeDocId: encodeDocId,
933 |
934 | /**
935 | * Accessing the root of a CouchDB instance returns meta information about
936 | * the instance. The response is a JSON structure containing information
937 | * about the server, including a welcome message and the version of the
938 | * server.
939 | * @see
941 | * docs for GET /
942 | * @param {ajaxSettings} options jQuery ajax settings
944 | */
945 | info: function(options) {
946 | ajax(
947 | {url: this.urlPrefix + "/"},
948 | options,
949 | "Server information could not be retrieved"
950 | );
951 | },
952 |
953 | /**
954 | * Request, configure, or stop, a replication operation.
955 | * @see docs for POST /_replicate
958 | * @param {String} source Path or url to source database
959 | * @param {String} target Path or url to target database
960 | * @param {ajaxSettings} ajaxOptions jQuery ajax settings
962 | * @param {Object} repOpts Additional replication options
963 | */
964 | replicate: function(source, target, ajaxOptions, repOpts) {
965 | repOpts = $.extend({source: source, target: target}, repOpts);
966 | if (repOpts.continuous && !repOpts.cancel) {
967 | ajaxOptions.successStatus = 202;
968 | }
969 | ajax({
970 | type: "POST", url: this.urlPrefix + "/_replicate",
971 | data: JSON.stringify(repOpts),
972 | contentType: "application/json"
973 | },
974 | ajaxOptions,
975 | "Replication failed"
976 | );
977 | },
978 |
979 | /**
980 | * Fetch a new UUID
981 | * @see docs for /_uuids
984 | * @param {Int} cacheNum Number of uuids to keep cached for future use
985 | */
986 | newUUID: function(cacheNum) {
987 | if (cacheNum === undefined) {
988 | cacheNum = 1;
989 | }
990 | if (!uuidCache.length) {
991 | ajax({url: this.urlPrefix + "/_uuids", data: {count: cacheNum}, async:
992 | false}, {
993 | success: function(resp) {
994 | uuidCache = resp.uuids;
995 | }
996 | },
997 | "Failed to retrieve UUID batch."
998 | );
999 | }
1000 | return uuidCache.shift();
1001 | }
1002 | });
1003 |
1004 | /**
1005 | * @private
1006 | */
1007 | function ajax(obj, options, errorMessage, ajaxOptions) {
1008 | options = $.extend({successStatus: 200}, options);
1009 | ajaxOptions = $.extend({contentType: "application/json"}, ajaxOptions);
1010 | errorMessage = errorMessage || "Unknown error";
1011 | $.ajax($.extend($.extend({
1012 | type: "GET", dataType: "json", cache : !$.browser.msie,
1013 | beforeSend: function(xhr){
1014 | if(ajaxOptions && ajaxOptions.headers){
1015 | for (var header in ajaxOptions.headers){
1016 | xhr.setRequestHeader(header, ajaxOptions.headers[header]);
1017 | }
1018 | }
1019 | },
1020 | complete: function(req) {
1021 | try {
1022 | var resp = $.parseJSON(req.responseText);
1023 | } catch(e) {
1024 | if (options.error) {
1025 | options.error(req.status, req, e);
1026 | } else {
1027 | alert(errorMessage + ": " + e);
1028 | }
1029 | return;
1030 | }
1031 | if (options.ajaxStart) {
1032 | options.ajaxStart(resp);
1033 | }
1034 | if (req.status == options.successStatus) {
1035 | if (options.beforeSuccess) options.beforeSuccess(req, resp);
1036 | if (options.success) options.success(resp);
1037 | } else if (options.error) {
1038 | options.error(req.status, resp && resp.error ||
1039 | errorMessage, resp && resp.reason || "no response");
1040 | } else {
1041 | alert(errorMessage + ": " + resp.reason);
1042 | }
1043 | }
1044 | }, obj), ajaxOptions));
1045 | }
1046 |
1047 | /**
1048 | * @private
1049 | */
1050 | function fullCommit(options) {
1051 | var options = options || {};
1052 | if (typeof options.ensure_full_commit !== "undefined") {
1053 | var commit = options.ensure_full_commit;
1054 | delete options.ensure_full_commit;
1055 | return function(xhr) {
1056 | xhr.setRequestHeader('Accept', 'application/json');
1057 | xhr.setRequestHeader("X-Couch-Full-Commit", commit.toString());
1058 | };
1059 | }
1060 | };
1061 |
1062 | /**
1063 | * @private
1064 | */
1065 | // Convert a options object to an url query string.
1066 | // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"'
1067 | function encodeOptions(options) {
1068 | var buf = [];
1069 | if (typeof(options) === "object" && options !== null) {
1070 | for (var name in options) {
1071 | if ($.inArray(name,
1072 | ["error", "success", "beforeSuccess", "ajaxStart"]) >= 0)
1073 | continue;
1074 | var value = options[name];
1075 | if ($.inArray(name, ["key", "startkey", "endkey"]) >= 0) {
1076 | value = toJSON(value);
1077 | }
1078 | buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
1079 | }
1080 | }
1081 | return buf.length ? "?" + buf.join("&") : "";
1082 | }
1083 |
1084 | /**
1085 | * @private
1086 | */
1087 | function toJSON(obj) {
1088 | return obj !== null ? JSON.stringify(obj) : null;
1089 | }
1090 |
1091 | })(jQuery);
1092 |
--------------------------------------------------------------------------------