diff options
author | Adam Kocoloski <adam@cloudant.com> | 2010-08-26 12:21:37 -0400 |
---|---|---|
committer | Adam Kocoloski <adam@cloudant.com> | 2010-08-26 12:21:37 -0400 |
commit | 949f9da6a6548704786d809be76a3a2d47d6fabe (patch) | |
tree | 8791c528310a88f4c01bd0d1da0872fb5bdfc3e6 /rel/overlay/share/www/script/jquery.couch.js | |
parent | ea7721877368bf0e356fe5601ce5c99a5460d12f (diff) |
move futon to share/www
Diffstat (limited to 'rel/overlay/share/www/script/jquery.couch.js')
-rw-r--r-- | rel/overlay/share/www/script/jquery.couch.js | 668 |
1 files changed, 668 insertions, 0 deletions
diff --git a/rel/overlay/share/www/script/jquery.couch.js b/rel/overlay/share/www/script/jquery.couch.js new file mode 100644 index 00000000..ebf7d52a --- /dev/null +++ b/rel/overlay/share/www/script/jquery.couch.js @@ -0,0 +1,668 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +(function($) { + $.couch = $.couch || {}; + + function encodeDocId(docID) { + var parts = docID.split("/"); + if (parts[0] == "_design") { + parts.shift(); + return "_design/" + encodeURIComponent(parts.join('/')); + } + return encodeURIComponent(docID); + }; + + function prepareUserDoc(user_doc, new_password) { + if (typeof hex_sha1 == "undefined") { + alert("creating a user doc requires sha1.js to be loaded in the page"); + return; + } + var user_prefix = "org.couchdb.user:"; + user_doc._id = user_doc._id || user_prefix + user_doc.name; + if (new_password) { + // handle the password crypto + user_doc.salt = $.couch.newUUID(); + user_doc.password_sha = hex_sha1(new_password + user_doc.salt); + } + user_doc.type = "user"; + if (!user_doc.roles) { + user_doc.roles = [] + } + return user_doc; + }; + + var uuidCache = []; + + $.extend($.couch, { + urlPrefix: '', + activeTasks: function(options) { + ajax( + {url: this.urlPrefix + "/_active_tasks"}, + options, + "Active task status could not be retrieved" + ); + }, + + allDbs: function(options) { + ajax( + {url: this.urlPrefix + "/_all_dbs"}, + options, + "An error occurred retrieving the list of all databases" + ); + }, + + config: function(options, section, option, value) { + var req = {url: this.urlPrefix + "/_config/"}; + if (section) { + req.url += encodeURIComponent(section) + "/"; + if (option) { + req.url += encodeURIComponent(option); + } + } + if (value === null) { + req.type = "DELETE"; + } else if (value !== undefined) { + req.type = "PUT"; + req.data = toJSON(value); + req.contentType = "application/json"; + req.processData = false + } + + ajax(req, options, + "An error occurred retrieving/updating the server configuration" + ); + }, + + session: function(options) { + options = options || {}; + $.ajax({ + type: "GET", url: this.urlPrefix + "/_session", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200) { + if (options.success) options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("An error occurred getting session info: " + resp.reason); + } + } + }); + }, + + userDb : function(callback) { + $.couch.session({ + success : function(resp) { + var userDb = $.couch.db(resp.info.authentication_db); + callback(userDb); + } + }); + }, + + signup: function(user_doc, password, options) { + options = options || {}; + // prepare user doc based on name and password + user_doc = prepareUserDoc(user_doc, password); + $.couch.userDb(function(db) { + db.saveDoc(user_doc, options); + }) + }, + + login: function(options) { + options = options || {}; + $.ajax({ + type: "POST", url: this.urlPrefix + "/_session", dataType: "json", + data: {name: options.name, password: options.password}, + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200) { + if (options.success) options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("An error occurred logging in: " + resp.reason); + } + } + }); + }, + logout: function(options) { + options = options || {}; + $.ajax({ + type: "DELETE", url: this.urlPrefix + "/_session", dataType: "json", + username : "_", password : "_", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200) { + if (options.success) options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("An error occurred logging out: " + resp.reason); + } + } + }); + }, + + db: function(name, db_opts) { + db_opts = db_opts || {}; + var rawDocs = {}; + function maybeApplyVersion(doc) { + if (doc._id && doc._rev && rawDocs[doc._id] && rawDocs[doc._id].rev == doc._rev) { + // todo: can we use commonjs require here? + if (typeof Base64 == "undefined") { + alert("please include /_utils/script/base64.js in the page for base64 support"); + return false; + } else { + doc._attachments = doc._attachments || {}; + doc._attachments["rev-"+doc._rev.split("-")[0]] = { + content_type :"application/json", + data : Base64.encode(rawDocs[doc._id].raw) + } + return true; + } + } + }; + return { + name: name, + uri: this.urlPrefix + "/" + encodeURIComponent(name) + "/", + + compact: function(options) { + $.extend(options, {successStatus: 202}); + ajax({ + type: "POST", url: this.uri + "_compact", + data: "", processData: false + }, + options, + "The database could not be compacted" + ); + }, + viewCleanup: function(options) { + $.extend(options, {successStatus: 202}); + ajax({ + type: "POST", url: this.uri + "_view_cleanup", + data: "", processData: false + }, + options, + "The views could not be cleaned up" + ); + }, + compactView: function(groupname, options) { + $.extend(options, {successStatus: 202}); + ajax({ + type: "POST", url: this.uri + "_compact/" + groupname, + data: "", processData: false + }, + options, + "The view could not be compacted" + ); + }, + create: function(options) { + $.extend(options, {successStatus: 201}); + ajax({ + type: "PUT", url: this.uri, contentType: "application/json", + data: "", processData: false + }, + options, + "The database could not be created" + ); + }, + drop: function(options) { + ajax( + {type: "DELETE", url: this.uri}, + options, + "The database could not be deleted" + ); + }, + info: function(options) { + ajax( + {url: this.uri}, + options, + "Database information could not be retrieved" + ); + }, + changes: function(since, options) { + options = options || {}; + // set up the promise object within a closure for this handler + var timeout = 100, db = this, active = true, + listeners = [], + promise = { + onChange : function(fun) { + listeners.push(fun); + }, + stop : function() { + active = false; + } + }; + // call each listener when there is a change + function triggerListeners(resp) { + $.each(listeners, function() { + this(resp); + }); + }; + // when there is a change, call any listeners, then check for another change + options.success = function(resp) { + timeout = 100; + if (active) { + since = resp.last_seq; + triggerListeners(resp); + getChangesSince(); + }; + }; + options.error = function() { + if (active) { + setTimeout(getChangesSince, timeout); + timeout = timeout * 2; + } + }; + // actually make the changes request + function getChangesSince() { + var opts = $.extend({heartbeat : 10 * 1000}, options, { + feed : "longpoll", + since : since + }); + ajax( + {url: db.uri + "_changes"+encodeOptions(opts)}, + options, + "Error connecting to "+db.uri+"/_changes." + ); + } + // start the first request + if (since) { + getChangesSince(); + } else { + db.info({ + success : function(info) { + since = info.update_seq; + getChangesSince(); + } + }); + } + return promise; + }, + allDocs: function(options) { + var type = "GET"; + var data = null; + if (options["keys"]) { + type = "POST"; + var keys = options["keys"]; + delete options["keys"]; + data = toJSON({ "keys": keys }); + } + ajax({ + type: type, + data: data, + url: this.uri + "_all_docs" + encodeOptions(options) + }, + options, + "An error occurred retrieving a list of all documents" + ); + }, + allDesignDocs: function(options) { + this.allDocs($.extend({startkey:"_design", endkey:"_design0"}, options)); + }, + allApps: function(options) { + options = options || {}; + var self = this; + if (options.eachApp) { + this.allDesignDocs({ + success: function(resp) { + $.each(resp.rows, function() { + self.openDoc(this.id, { + success: function(ddoc) { + var index, appPath, appName = ddoc._id.split('/'); + appName.shift(); + appName = appName.join('/'); + index = ddoc.couchapp && ddoc.couchapp.index; + if (index) { + appPath = ['', name, ddoc._id, index].join('/'); + } else if (ddoc._attachments && ddoc._attachments["index.html"]) { + appPath = ['', name, ddoc._id, "index.html"].join('/'); + } + if (appPath) options.eachApp(appName, appPath, ddoc); + } + }); + }); + } + }); + } else { + alert("Please provide an eachApp function for allApps()"); + } + }, + openDoc: function(docId, options, ajaxOptions) { + options = options || {}; + if (db_opts.attachPrevRev || options.attachPrevRev) { + $.extend(options, { + beforeSuccess : function(req, doc) { + rawDocs[doc._id] = { + rev : doc._rev, + raw : req.responseText + }; + } + }); + } else { + $.extend(options, { + beforeSuccess : function(req, doc) { + if (doc["jquery.couch.attachPrevRev"]) { + rawDocs[doc._id] = { + rev : doc._rev, + raw : req.responseText + }; + } + } + }); + } + ajax({url: this.uri + encodeDocId(docId) + encodeOptions(options)}, + options, + "The document could not be retrieved", + ajaxOptions + ); + }, + saveDoc: function(doc, options) { + options = options || {}; + var db = this; + var beforeSend = fullCommit(options); + if (doc._id === undefined) { + var method = "POST"; + var uri = this.uri; + } else { + var method = "PUT"; + var uri = this.uri + encodeDocId(doc._id); + } + var versioned = maybeApplyVersion(doc); + $.ajax({ + type: method, url: uri + encodeOptions(options), + contentType: "application/json", + dataType: "json", data: toJSON(doc), + beforeSend : beforeSend, + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200 || req.status == 201 || req.status == 202) { + doc._id = resp.id; + doc._rev = resp.rev; + if (versioned) { + db.openDoc(doc._id, { + attachPrevRev : true, + success : function(d) { + doc._attachments = d._attachments; + if (options.success) options.success(resp); + } + }); + } else { + if (options.success) options.success(resp); + } + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("The document could not be saved: " + resp.reason); + } + } + }); + }, + bulkSave: function(docs, options) { + var beforeSend = fullCommit(options); + $.extend(options, {successStatus: 201, beforeSend : beforeSend}); + ajax({ + type: "POST", + url: this.uri + "_bulk_docs" + encodeOptions(options), + contentType: "application/json", data: toJSON(docs) + }, + options, + "The documents could not be saved" + ); + }, + removeDoc: function(doc, options) { + ajax({ + type: "DELETE", + url: this.uri + + encodeDocId(doc._id) + + encodeOptions({rev: doc._rev}) + }, + options, + "The document could not be deleted" + ); + }, + bulkRemove: function(docs, options){ + docs.docs = $.each( + docs.docs, function(i, doc){ + doc._deleted = true; + } + ); + $.extend(options, {successStatus: 201}); + ajax({ + type: "POST", + url: this.uri + "_bulk_docs" + encodeOptions(options), + data: toJSON(docs) + }, + options, + "The documents could not be deleted" + ); + }, + copyDoc: function(docId, options, ajaxOptions) { + ajaxOptions = $.extend(ajaxOptions, { + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 201) { + if (options.success) options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("The document could not be copied: " + resp.reason); + } + } + }); + ajax({ + type: "COPY", + url: this.uri + encodeDocId(docId) + }, + options, + "The document could not be copied", + ajaxOptions + ); + }, + query: function(mapFun, reduceFun, language, options) { + language = language || "javascript"; + if (typeof(mapFun) !== "string") { + mapFun = mapFun.toSource ? mapFun.toSource() : "(" + mapFun.toString() + ")"; + } + var body = {language: language, map: mapFun}; + if (reduceFun != null) { + if (typeof(reduceFun) !== "string") + reduceFun = reduceFun.toSource ? reduceFun.toSource() : "(" + reduceFun.toString() + ")"; + body.reduce = reduceFun; + } + ajax({ + type: "POST", + url: this.uri + "_temp_view" + encodeOptions(options), + contentType: "application/json", data: toJSON(body) + }, + options, + "An error occurred querying the database" + ); + }, + list: function(list, view, options) { + var list = list.split('/'); + var options = options || {}; + var type = 'GET'; + var data = null; + if (options['keys']) { + type = 'POST'; + var keys = options['keys']; + delete options['keys']; + data = toJSON({'keys': keys }); + } + ajax({ + type: type, + data: data, + url: this.uri + '_design/' + list[0] + + '/_list/' + list[1] + '/' + view + encodeOptions(options) + }, + options, 'An error occured accessing the list' + ); + }, + view: function(name, options) { + var name = name.split('/'); + var options = options || {}; + var type = "GET"; + var data= null; + if (options["keys"]) { + type = "POST"; + var keys = options["keys"]; + delete options["keys"]; + data = toJSON({ "keys": keys }); + } + ajax({ + type: type, + data: data, + url: this.uri + "_design/" + name[0] + + "/_view/" + name[1] + encodeOptions(options) + }, + options, "An error occurred accessing the view" + ); + }, + getDbProperty: function(propName, options, ajaxOptions) { + ajax({url: this.uri + propName + encodeOptions(options)}, + options, + "The property could not be retrieved", + ajaxOptions + ); + }, + + setDbProperty: function(propName, propValue, options, ajaxOptions) { + ajax({ + type: "PUT", + url: this.uri + propName + encodeOptions(options), + data : JSON.stringify(propValue) + }, + options, + "The property could not be updated", + ajaxOptions + ); + } + }; + }, + + encodeDocId: encodeDocId, + + info: function(options) { + ajax( + {url: this.urlPrefix + "/"}, + options, + "Server information could not be retrieved" + ); + }, + + replicate: function(source, target, ajaxOptions, repOpts) { + repOpts = $.extend({source: source, target: target}, repOpts); + if (repOpts.continuous) { + ajaxOptions.successStatus = 202; + } + ajax({ + type: "POST", url: this.urlPrefix + "/_replicate", + data: JSON.stringify(repOpts), + contentType: "application/json" + }, + ajaxOptions, + "Replication failed" + ); + }, + + newUUID: function(cacheNum) { + if (cacheNum === undefined) { + cacheNum = 1; + } + if (!uuidCache.length) { + ajax({url: this.urlPrefix + "/_uuids", data: {count: cacheNum}, async: false}, { + success: function(resp) { + uuidCache = resp.uuids + } + }, + "Failed to retrieve UUID batch." + ); + } + return uuidCache.shift(); + } + }); + + function ajax(obj, options, errorMessage, ajaxOptions) { + options = $.extend({successStatus: 200}, options); + ajaxOptions = $.extend({contentType: "application/json"}, ajaxOptions); + errorMessage = errorMessage || "Unknown error"; + $.ajax($.extend($.extend({ + type: "GET", dataType: "json", cache : !$.browser.msie, + beforeSend: function(xhr){ + if(ajaxOptions && ajaxOptions.headers){ + for (var header in ajaxOptions.headers){ + xhr.setRequestHeader(header, ajaxOptions.headers[header]); + } + } + }, + complete: function(req) { + try { + var resp = $.httpData(req, "json"); + } catch(e) { + if (options.error) { + options.error(req.status, req, e); + } else { + alert(errorMessage + ": " + e); + } + return; + } + if (options.ajaxStart) { + options.ajaxStart(resp); + } + if (req.status == options.successStatus) { + if (options.beforeSuccess) options.beforeSuccess(req, resp); + if (options.success) options.success(resp); + } else if (options.error) { + options.error(req.status, resp && resp.error || errorMessage, resp && resp.reason || "no response"); + } else { + alert(errorMessage + ": " + resp.reason); + } + } + }, obj), ajaxOptions)); + } + + function fullCommit(options) { + var options = options || {}; + if (typeof options.ensure_full_commit !== "undefined") { + var commit = options.ensure_full_commit; + delete options.ensure_full_commit; + return function(xhr) { + xhr.setRequestHeader("X-Couch-Full-Commit", commit.toString()); + }; + } + }; + + // Convert a options object to an url query string. + // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"' + function encodeOptions(options) { + var buf = []; + if (typeof(options) === "object" && options !== null) { + for (var name in options) { + if ($.inArray(name, ["error", "success", "beforeSuccess", "ajaxStart"]) >= 0) + continue; + var value = options[name]; + if ($.inArray(name, ["key", "startkey", "endkey"]) >= 0) { + value = toJSON(value); + } + buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value)); + } + } + return buf.length ? "?" + buf.join("&") : ""; + } + + function toJSON(obj) { + return obj !== null ? JSON.stringify(obj) : null; + } + +})(jQuery); |