PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/share/www/script/couch.js

http://github.com/halorgium/couchdb
JavaScript | 483 lines | 401 code | 50 blank | 32 comment | 85 complexity | fa540c124f5275bd77b20064cfb857a4 MD5 | raw file
Possible License(s): Apache-2.0
  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. // A simple class to represent a database. Uses XMLHttpRequest to interface with
  13. // the CouchDB server.
  14. function CouchDB(name, httpHeaders) {
  15. this.name = name;
  16. this.uri = "/" + encodeURIComponent(name) + "/";
  17. // The XMLHttpRequest object from the most recent request. Callers can
  18. // use this to check result http status and headers.
  19. this.last_req = null;
  20. this.request = function(method, uri, requestOptions) {
  21. requestOptions = requestOptions || {}
  22. requestOptions.headers = combine(requestOptions.headers, httpHeaders)
  23. return CouchDB.request(method, uri, requestOptions);
  24. }
  25. // Creates the database on the server
  26. this.createDb = function() {
  27. this.last_req = this.request("PUT", this.uri);
  28. CouchDB.maybeThrowError(this.last_req);
  29. return JSON.parse(this.last_req.responseText);
  30. }
  31. // Deletes the database on the server
  32. this.deleteDb = function() {
  33. this.last_req = this.request("DELETE", this.uri);
  34. if (this.last_req.status == 404) {
  35. return false;
  36. }
  37. CouchDB.maybeThrowError(this.last_req);
  38. return JSON.parse(this.last_req.responseText);
  39. }
  40. // Save a document to the database
  41. this.save = function(doc, options) {
  42. if (doc._id == undefined) {
  43. doc._id = CouchDB.newUuids(1)[0];
  44. }
  45. this.last_req = this.request("PUT", this.uri +
  46. encodeURIComponent(doc._id) + encodeOptions(options),
  47. {body: JSON.stringify(doc)});
  48. CouchDB.maybeThrowError(this.last_req);
  49. var result = JSON.parse(this.last_req.responseText);
  50. doc._rev = result.rev;
  51. return result;
  52. }
  53. // Open a document from the database
  54. this.open = function(docId, options) {
  55. this.last_req = this.request("GET", this.uri + encodeURIComponent(docId)
  56. + encodeOptions(options));
  57. if (this.last_req.status == 404) {
  58. return null;
  59. }
  60. CouchDB.maybeThrowError(this.last_req);
  61. return JSON.parse(this.last_req.responseText);
  62. }
  63. // Deletes a document from the database
  64. this.deleteDoc = function(doc) {
  65. this.last_req = this.request("DELETE", this.uri + encodeURIComponent(doc._id)
  66. + "?rev=" + doc._rev);
  67. CouchDB.maybeThrowError(this.last_req);
  68. var result = JSON.parse(this.last_req.responseText);
  69. doc._rev = result.rev; //record rev in input document
  70. doc._deleted = true;
  71. return result;
  72. }
  73. // Deletes an attachment from a document
  74. this.deleteDocAttachment = function(doc, attachment_name) {
  75. this.last_req = this.request("DELETE", this.uri + encodeURIComponent(doc._id)
  76. + "/" + attachment_name + "?rev=" + doc._rev);
  77. CouchDB.maybeThrowError(this.last_req);
  78. var result = JSON.parse(this.last_req.responseText);
  79. doc._rev = result.rev; //record rev in input document
  80. return result;
  81. }
  82. this.bulkSave = function(docs, options) {
  83. // first prepoulate the UUIDs for new documents
  84. var newCount = 0
  85. for (var i=0; i<docs.length; i++) {
  86. if (docs[i]._id == undefined) {
  87. newCount++;
  88. }
  89. }
  90. var newUuids = CouchDB.newUuids(docs.length);
  91. var newCount = 0
  92. for (var i=0; i<docs.length; i++) {
  93. if (docs[i]._id == undefined) {
  94. docs[i]._id = newUuids.pop();
  95. }
  96. }
  97. var json = {"docs": docs};
  98. // put any options in the json
  99. for (var option in options) {
  100. json[option] = options[option];
  101. }
  102. this.last_req = this.request("POST", this.uri + "_bulk_docs", {
  103. body: JSON.stringify(json)
  104. });
  105. if (this.last_req.status == 417) {
  106. return {errors: JSON.parse(this.last_req.responseText)};
  107. }
  108. else {
  109. CouchDB.maybeThrowError(this.last_req);
  110. var results = JSON.parse(this.last_req.responseText);
  111. for (var i = 0; i < docs.length; i++) {
  112. if(results[i] && results[i].rev) {
  113. docs[i]._rev = results[i].rev;
  114. }
  115. }
  116. return results;
  117. }
  118. }
  119. this.ensureFullCommit = function() {
  120. this.last_req = this.request("POST", this.uri + "_ensure_full_commit");
  121. CouchDB.maybeThrowError(this.last_req);
  122. return JSON.parse(this.last_req.responseText);
  123. }
  124. // Applies the map function to the contents of database and returns the results.
  125. this.query = function(mapFun, reduceFun, options, keys, language) {
  126. var body = {language: language || "javascript"};
  127. if(keys) {
  128. body.keys = keys ;
  129. }
  130. if (typeof(mapFun) != "string") {
  131. mapFun = mapFun.toSource ? mapFun.toSource() : "(" + mapFun.toString() + ")";
  132. }
  133. body.map = mapFun;
  134. if (reduceFun != null) {
  135. if (typeof(reduceFun) != "string") {
  136. reduceFun = reduceFun.toSource ?
  137. reduceFun.toSource() : "(" + reduceFun.toString() + ")";
  138. }
  139. body.reduce = reduceFun;
  140. }
  141. if (options && options.options != undefined) {
  142. body.options = options.options;
  143. delete options.options;
  144. }
  145. this.last_req = this.request("POST", this.uri + "_temp_view"
  146. + encodeOptions(options), {
  147. headers: {"Content-Type": "application/json"},
  148. body: JSON.stringify(body)
  149. });
  150. CouchDB.maybeThrowError(this.last_req);
  151. return JSON.parse(this.last_req.responseText);
  152. }
  153. this.view = function(viewname, options, keys) {
  154. var viewParts = viewname.split('/');
  155. var viewPath = this.uri + "_design/" + viewParts[0] + "/_view/"
  156. + viewParts[1] + encodeOptions(options);
  157. if(!keys) {
  158. this.last_req = this.request("GET", viewPath);
  159. } else {
  160. this.last_req = this.request("POST", viewPath, {
  161. headers: {"Content-Type": "application/json"},
  162. body: JSON.stringify({keys:keys})
  163. });
  164. }
  165. if (this.last_req.status == 404) {
  166. return null;
  167. }
  168. CouchDB.maybeThrowError(this.last_req);
  169. return JSON.parse(this.last_req.responseText);
  170. }
  171. // gets information about the database
  172. this.info = function() {
  173. this.last_req = this.request("GET", this.uri);
  174. CouchDB.maybeThrowError(this.last_req);
  175. return JSON.parse(this.last_req.responseText);
  176. }
  177. // gets information about a design doc
  178. this.designInfo = function(docid) {
  179. this.last_req = this.request("GET", this.uri + docid + "/_info");
  180. CouchDB.maybeThrowError(this.last_req);
  181. return JSON.parse(this.last_req.responseText);
  182. }
  183. this.viewCleanup = function() {
  184. this.last_req = this.request("POST", this.uri + "_view_cleanup");
  185. CouchDB.maybeThrowError(this.last_req);
  186. return JSON.parse(this.last_req.responseText);
  187. }
  188. this.allDocs = function(options,keys) {
  189. if(!keys) {
  190. this.last_req = this.request("GET", this.uri + "_all_docs"
  191. + encodeOptions(options));
  192. } else {
  193. this.last_req = this.request("POST", this.uri + "_all_docs"
  194. + encodeOptions(options), {
  195. headers: {"Content-Type": "application/json"},
  196. body: JSON.stringify({keys:keys})
  197. });
  198. }
  199. CouchDB.maybeThrowError(this.last_req);
  200. return JSON.parse(this.last_req.responseText);
  201. }
  202. this.designDocs = function() {
  203. return this.allDocs({startkey:"_design", endkey:"_design0"});
  204. };
  205. this.changes = function(options,keys) {
  206. var req = null;
  207. if(!keys) {
  208. req = this.request("GET", this.uri + "_changes" + encodeOptions(options));
  209. } else {
  210. req = this.request("POST", this.uri + "_changes" + encodeOptions(options), {
  211. headers: {"Content-Type": "application/json"},
  212. body: JSON.stringify({keys:keys})
  213. });
  214. }
  215. CouchDB.maybeThrowError(req);
  216. return JSON.parse(req.responseText);
  217. }
  218. this.compact = function() {
  219. this.last_req = this.request("POST", this.uri + "_compact");
  220. CouchDB.maybeThrowError(this.last_req);
  221. return JSON.parse(this.last_req.responseText);
  222. }
  223. this.viewCleanup = function() {
  224. this.last_req = this.request("POST", this.uri + "_view_cleanup");
  225. CouchDB.maybeThrowError(this.last_req);
  226. return JSON.parse(this.last_req.responseText);
  227. }
  228. this.setDbProperty = function(propId, propValue) {
  229. this.last_req = this.request("PUT", this.uri + propId,{
  230. body:JSON.stringify(propValue)
  231. });
  232. CouchDB.maybeThrowError(this.last_req);
  233. return JSON.parse(this.last_req.responseText);
  234. }
  235. this.getDbProperty = function(propId) {
  236. this.last_req = this.request("GET", this.uri + propId);
  237. CouchDB.maybeThrowError(this.last_req);
  238. return JSON.parse(this.last_req.responseText);
  239. }
  240. this.setSecObj = function(secObj) {
  241. this.last_req = this.request("PUT", this.uri + "_security",{
  242. body:JSON.stringify(secObj)
  243. });
  244. CouchDB.maybeThrowError(this.last_req);
  245. return JSON.parse(this.last_req.responseText);
  246. }
  247. this.getSecObj = function() {
  248. this.last_req = this.request("GET", this.uri + "_security");
  249. CouchDB.maybeThrowError(this.last_req);
  250. return JSON.parse(this.last_req.responseText);
  251. }
  252. // Convert a options object to an url query string.
  253. // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"'
  254. function encodeOptions(options) {
  255. var buf = []
  256. if (typeof(options) == "object" && options !== null) {
  257. for (var name in options) {
  258. if (!options.hasOwnProperty(name)) { continue };
  259. var value = options[name];
  260. if (name == "key" || name == "startkey" || name == "endkey") {
  261. value = toJSON(value);
  262. }
  263. buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
  264. }
  265. }
  266. if (!buf.length) {
  267. return "";
  268. }
  269. return "?" + buf.join("&");
  270. }
  271. function toJSON(obj) {
  272. return obj !== null ? JSON.stringify(obj) : null;
  273. }
  274. function combine(object1, object2) {
  275. if (!object2) {
  276. return object1;
  277. }
  278. if (!object1) {
  279. return object2;
  280. }
  281. for (var name in object2) {
  282. object1[name] = object2[name];
  283. }
  284. return object1;
  285. }
  286. }
  287. // this is the XMLHttpRequest object from last request made by the following
  288. // CouchDB.* functions (except for calls to request itself).
  289. // Use this from callers to check HTTP status or header values of requests.
  290. CouchDB.last_req = null;
  291. CouchDB.urlPrefix = '';
  292. CouchDB.login = function(name, password) {
  293. CouchDB.last_req = CouchDB.request("POST", "/_session", {
  294. headers: {"Content-Type": "application/x-www-form-urlencoded",
  295. "X-CouchDB-WWW-Authenticate": "Cookie"},
  296. body: "name=" + encodeURIComponent(name) + "&password="
  297. + encodeURIComponent(password)
  298. });
  299. return JSON.parse(CouchDB.last_req.responseText);
  300. }
  301. CouchDB.logout = function() {
  302. CouchDB.last_req = CouchDB.request("DELETE", "/_session", {
  303. headers: {"Content-Type": "application/x-www-form-urlencoded",
  304. "X-CouchDB-WWW-Authenticate": "Cookie"}
  305. });
  306. return JSON.parse(CouchDB.last_req.responseText);
  307. }
  308. CouchDB.session = function(options) {
  309. options = options || {};
  310. CouchDB.last_req = CouchDB.request("GET", "/_session", options);
  311. CouchDB.maybeThrowError(CouchDB.last_req);
  312. return JSON.parse(CouchDB.last_req.responseText);
  313. };
  314. CouchDB.user_prefix = "org.couchdb.user:";
  315. CouchDB.prepareUserDoc = function(user_doc, new_password) {
  316. user_doc._id = user_doc._id || CouchDB.user_prefix + user_doc.name;
  317. if (new_password) {
  318. // handle the password crypto
  319. user_doc.salt = CouchDB.newUuids(1)[0];
  320. user_doc.password_sha = hex_sha1(new_password + user_doc.salt);
  321. }
  322. user_doc.type = "user";
  323. if (!user_doc.roles) {
  324. user_doc.roles = []
  325. }
  326. return user_doc;
  327. };
  328. CouchDB.allDbs = function() {
  329. CouchDB.last_req = CouchDB.request("GET", "/_all_dbs");
  330. CouchDB.maybeThrowError(CouchDB.last_req);
  331. return JSON.parse(CouchDB.last_req.responseText);
  332. };
  333. CouchDB.allDesignDocs = function() {
  334. var ddocs = {}, dbs = CouchDB.allDbs();
  335. for (var i=0; i < dbs.length; i++) {
  336. var db = new CouchDB(dbs[i]);
  337. ddocs[dbs[i]] = db.designDocs();
  338. };
  339. return ddocs;
  340. };
  341. CouchDB.getVersion = function() {
  342. CouchDB.last_req = CouchDB.request("GET", "/");
  343. CouchDB.maybeThrowError(CouchDB.last_req);
  344. return JSON.parse(CouchDB.last_req.responseText).version;
  345. }
  346. CouchDB.replicate = function(source, target, rep_options) {
  347. rep_options = rep_options || {};
  348. var headers = rep_options.headers || {};
  349. var body = rep_options.body || {};
  350. body.source = source;
  351. body.target = target;
  352. CouchDB.last_req = CouchDB.request("POST", "/_replicate", {
  353. headers: headers,
  354. body: JSON.stringify(body)
  355. });
  356. CouchDB.maybeThrowError(CouchDB.last_req);
  357. return JSON.parse(CouchDB.last_req.responseText);
  358. }
  359. CouchDB.newXhr = function() {
  360. if (typeof(XMLHttpRequest) != "undefined") {
  361. return new XMLHttpRequest();
  362. } else if (typeof(ActiveXObject) != "undefined") {
  363. return new ActiveXObject("Microsoft.XMLHTTP");
  364. } else {
  365. throw new Error("No XMLHTTPRequest support detected");
  366. }
  367. }
  368. CouchDB.request = function(method, uri, options) {
  369. options = options || {};
  370. var req = CouchDB.newXhr();
  371. if(uri.substr(0, "http://".length) != "http://") {
  372. uri = CouchDB.urlPrefix + uri
  373. }
  374. req.open(method, uri, false);
  375. if (options.headers) {
  376. var headers = options.headers;
  377. for (var headerName in headers) {
  378. if (!headers.hasOwnProperty(headerName)) { continue; }
  379. req.setRequestHeader(headerName, headers[headerName]);
  380. }
  381. }
  382. req.send(options.body || "");
  383. return req;
  384. }
  385. CouchDB.requestStats = function(module, key, test) {
  386. var query_arg = "";
  387. if(test !== null) {
  388. query_arg = "?flush=true";
  389. }
  390. var url = "/_stats/" + module + "/" + key + query_arg;
  391. var stat = CouchDB.request("GET", url).responseText;
  392. return JSON.parse(stat)[module][key];
  393. }
  394. CouchDB.uuids_cache = [];
  395. CouchDB.newUuids = function(n, buf) {
  396. buf = buf || 100;
  397. if (CouchDB.uuids_cache.length >= n) {
  398. var uuids = CouchDB.uuids_cache.slice(CouchDB.uuids_cache.length - n);
  399. if(CouchDB.uuids_cache.length - n == 0) {
  400. CouchDB.uuids_cache = [];
  401. } else {
  402. CouchDB.uuids_cache =
  403. CouchDB.uuids_cache.slice(0, CouchDB.uuids_cache.length - n);
  404. }
  405. return uuids;
  406. } else {
  407. CouchDB.last_req = CouchDB.request("GET", "/_uuids?count=" + (buf + n));
  408. CouchDB.maybeThrowError(CouchDB.last_req);
  409. var result = JSON.parse(CouchDB.last_req.responseText);
  410. CouchDB.uuids_cache =
  411. CouchDB.uuids_cache.concat(result.uuids.slice(0, buf));
  412. return result.uuids.slice(buf);
  413. }
  414. }
  415. CouchDB.maybeThrowError = function(req) {
  416. if (req.status >= 400) {
  417. try {
  418. var result = JSON.parse(req.responseText);
  419. } catch (ParseError) {
  420. var result = {error:"unknown", reason:req.responseText};
  421. }
  422. throw result;
  423. }
  424. }
  425. CouchDB.params = function(options) {
  426. options = options || {};
  427. var returnArray = [];
  428. for(var key in options) {
  429. var value = options[key];
  430. returnArray.push(key + "=" + value);
  431. }
  432. return returnArray.join("&");
  433. };