Top Banner

of 21

couch-js

Apr 06, 2018

Download

Documents

Aditya Nugraha
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
  • 8/3/2019 couch-js

    1/21

    // 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.

    /**

    * @namespace* $.couch is used to communicate with a CouchDB server, the server methods can* be called directly without creating an instance. Typically all methods are* passed an options object which defines a success callback which* is called with the data returned from the http request to CouchDB, you can* find the other settings that can be used in the options object

    * from * jQuery.ajax settings* $.couch.activeTasks({* success: function (data) {

    * console.log(data);* }

    * });* Outputs (for example):* [* {

    * "pid" : "",* "status" : "Copied 0 of 18369 changes (0%)",* "task" : "recipes",* "type" : "Database Compaction"

    * }*]

    */(function($) {

    $.couch = $.couch || {};

    /** @lends $.couch */

    /*** @private*/function encodeDocId(docID) {

    var parts = docID.split("/");if (parts[0] == "_design") {parts.shift();return "_design/" + encodeURIComponent(parts.join('/'));

    }return encodeURIComponent(docID);

    }

    /**

  • 8/3/2019 couch-js

    2/21

    * @private*/

    var uuidCache = [];

    $.extend($.couch, {

    urlPrefix: '',

    /**

    * You can obtain a list of active tasks by using the /_active_tasks URL.* The result is a JSON array of the currently running tasks, with each task* being described with a single object.* @see docs for /_active_tasks* @param {ajaxSettings} options jQuery ajax settings*/activeTasks: function(options) {

    ajax({url: this.urlPrefix + "/_active_tasks"},options,"Active task status could not be retrieved"

    );},

    /*** Returns a list of all the databases in the CouchDB instance* @see docs for /_all_dbs* @param {ajaxSettings} options jQuery ajax settings

    */allDbs: function(options) {

    ajax({url: this.urlPrefix + "/_all_dbs"},options,"An error occurred retrieving the list of all databases"

    );

    },

    /*** View and edit the CouchDB configuration, called with just the options* parameter the entire config is returned, you can be more specific by

    * passing the section and option parameters, if you specify a value that* value will be stored in the configuration.* @see docs for /_config* @param {ajaxSettings} options

    * * jQuery ajax settings* @param {String} [section] the section of the config

  • 8/3/2019 couch-js

    3/21

    * @param {String} [option] the particular config option* @param {String} [value] value to be set*/

    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"

    );

    },

    /*** Returns the session information for the currently logged in user.* @param {ajaxSettings} options*

    * jQuery ajax settings*/session: function(options) {options = options || {};

    $.ajax({type: "GET", url: this.urlPrefix + "/_session",

    beforeSend: function(xhr) {xhr.setRequestHeader('Accept', 'application/json');

    },complete: function(req) {

    var resp = $.parseJSON(req.responseText);

    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);}

    }});

    },

    /*** @private*/

  • 8/3/2019 couch-js

    4/21

    userDb : function(callback) {$.couch.session({success : function(resp) {

    var userDb = $.couch.db(resp.info.authentication_db);callback(userDb);

    }

    });},

    /*** Create a new user on the CouchDB server, user_doc is an* object with a name field and other information you want* to store relating to that user, for example

    * {"name": "daleharvey"}* @param {Object} user_doc Users details* @param {String} password Users password* @param {ajaxSettings} options* * jQuery ajax settings

    */signup: function(user_doc, password, options) {options = options || {};// prepare user doc based on name and password

    user_doc = this.prepareUserDoc(user_doc, password);$.couch.userDb(function(db) {

    db.saveDoc(user_doc, options);});

    },

    /*** Populates a user doc with a new password.* @param {Object} user_doc User details* @param {String} new_password New Password

    */prepareUserDoc: function(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 cryptouser_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;

    },

    /**

  • 8/3/2019 couch-js

    5/21

    * Authenticate against CouchDB, the options parameter is*expected to have name and password fields.

    * @param {ajaxSettings} options

    * * jQuery ajax settings*/

    login: function(options) {options = options || {};$.ajax({

    type: "POST", url: this.urlPrefix + "/_session", dataType: "json",data: {name: options.name, password: options.password},beforeSend: function(xhr) {

    xhr.setRequestHeader('Accept', 'application/json');

    },complete: function(req) {var resp = $.parseJSON(req.responseText);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);

    }

    }});

    },

    /**

    * Delete your current CouchDB user session* @param {ajaxSettings} options* * jQuery ajax settings

    */logout: function(options) {

    options = options || {};$.ajax({type: "DELETE", url: this.urlPrefix + "/_session", dataType: "json",username : "_", password : "_",

    beforeSend: function(xhr) {

    xhr.setRequestHeader('Accept', 'application/json');},complete: function(req) {var resp = $.parseJSON(req.responseText);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);}

    }});

    },

  • 8/3/2019 couch-js

    6/21

  • 8/3/2019 couch-js

    7/21

    );},

    /*** Cleans up the cached view output on disk for a given view.* @see docs for /db/_compact* @param {ajaxSettings} options jQuery ajax settings*/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"

    );},

    /**

    * Compacts the view indexes associated with the specified design* document. You can use this in place of the full database compaction

    * if you know a specific set of view indexes have been affected by a* recent database change.* @see docs for /db/_compact/design-doc* @param {String} groupname Name of design-doc to compact* @param {ajaxSettings} options jQuery ajax settings

    */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 a new database* @see docs for PUT /db/* @param {ajaxSettings} options jQuery ajax settings*/create: function(options) {

  • 8/3/2019 couch-js

    8/21

    $.extend(options, {successStatus: 201});ajax({

    type: "PUT", url: this.uri, contentType: "application/json",

    data: "", processData: false},options,

    "The database could not be created");},

    /*** Deletes the specified database, and all the documents and* attachments contained within it.

    * @see docs for DELETE /db/* @param {ajaxSettings} options jQuery ajax settings*/

    drop: function(options) {ajax({type: "DELETE", url: this.uri},options,

    "The database could not be deleted");

    },

    /*** Gets information about the specified database.

    * @see docs for GET /db/* @param {ajaxSettings} options jQuery ajax settings*/

    info: function(options) {ajax({url: this.uri},options,

    "Database information could not be retrieved"

    );},

    /*** @namespace

    * $.couch.db.changes provides an API for subscribing to the changes* feed* var $changes = $.couch.db("mydatabase").changes();*$changes.onChange = function (data) {

    * ... process data ...* }

    * $changes.stop();* */

  • 8/3/2019 couch-js

    9/21

    changes: function(since, options) {

    options = options || {};

    // set up the promise object within a closure for this handlervar timeout = 100, db = this, active = true,listeners = [],

    promise = /** @lends $.couch.db.changes */ {/*** Add a listener callback

    * @see docs for /db/_changes* @param {Function} fun Callback function to run when

    * notified of changes.*/

    onChange : function(fun) {listeners.push(fun);

    },/**

    * Stop subscribing to the changes feed*/

    stop : function() {active = false;

    }};

    // call each listener when there is a changefunction 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 requestfunction getChangesSince() {

    var opts = $.extend({heartbeat : 10 * 1000}, options, {feed : "longpoll",

    since : since});ajax(

  • 8/3/2019 couch-js

    10/21

    {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;

    },

    /*** Fetch all the docs in this db, you can specify an array of keys to* fetch by passing the keys field in the* options

    * parameter.* @see docs for /db/all_docs/* @param {ajaxSettings} options jQuery ajax settings

    */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");

    },

    /*** Fetch all the design docs in this db

    * @param {ajaxSettings} options jQuery ajax settings*/

  • 8/3/2019 couch-js

    11/21

    allDesignDocs: function(options) {this.allDocs($.extend({startkey:"_design", endkey:"_design0"}, options));

    },

    /**

    * Fetch all the design docs with an index.html, options* parameter expects an eachApp field which is a callback* called on each app found.

    * @param {ajaxSettings} options jQuery ajax settings*/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()");

    }

    },

    /*** Returns the specified doc from the specified db.* @see docs for GET /db/doc* @param {String} docId id of document to fetch* @param {ajaxSettings} options jQuery ajax settings* @param {ajaxSettings} ajaxOptions jQuery ajax settings*/openDoc: function(docId, options, ajaxOptions) {

  • 8/3/2019 couch-js

    12/21

    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);

    },

    /*** Create a new document in the specified database, using the supplied* JSON document structure. If the JSON structure includes the _id* field, then the document will be created with the specified document

    * ID. If the _id field is not specified, a new unique ID will be* generated.

    * @see docs for GET /db/doc* @param {String} doc document to save

    * @param {ajaxSettings} options jQuery ajax settings*/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);

  • 8/3/2019 couch-js

    13/21

    $.ajax({type: method, url: uri + encodeOptions(options),contentType: "application/json",

    dataType: "json", data: toJSON(doc),beforeSend : beforeSend,complete: function(req) {

    var resp = $.parseJSON(req.responseText);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);

    }}

    });},

    /*** Save a list of documents* @see docs for /db/_bulk_docs

    * @param {Object[]} docs List of documents to save* @param {ajaxSettings} options jQuery ajax settings*/

    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"

    );},

    /*** Deletes the specified document from the database. You must supply

  • 8/3/2019 couch-js

    14/21

  • 8/3/2019 couch-js

    15/21

    * @param {ajaxSettings} options jQuery ajax settings* @param {ajaxSettings} options jQuery ajax settings*/copyDoc: function(docId, options, ajaxOptions) {

    ajaxOptions = $.extend(ajaxOptions, {complete: function(req) {var resp = $.parseJSON(req.responseText);

    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

    );},

    /**

    * Creates (and executes) a temporary view based on the view function* supplied in the JSON request.* @see docs for /db/_temp_view* @param {Function} mapFun Map function

    * @param {Function} reduceFun Reduce function* @param {Function} language Language the map / reduce funs are* implemented in* @param {ajaxSettings} options jQuery ajax settings

    */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;

    }

  • 8/3/2019 couch-js

    16/21

    ajax({type: "POST",url: this.uri + "_temp_view" + encodeOptions(options),

    contentType: "application/json", data: toJSON(body)},options,

    "An error occurred querying the database");},

    /*** Fetch a _list view output, you can specify a list of* keys in the options object to recieve only those keys.

    * @see * docs for /db/_design/design-doc/_list/l1/v1* @param {String} list Listname in the form of ddoc/listname* @param {String} view View to run list against

    * @param {options} CouchDB View Options* @param {ajaxSettings} options jQuery ajax settings

    */list: function(list, view, options, ajaxOptions) {

    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)

    },ajaxOptions, 'An error occured accessing the list'

    );},

    /*** Executes the specified view-name from the specified design-doc* design document, you can specify a list of keys* in the options object to recieve only those keys.

    * @see docs for /db/* _design/design-doc/_list/l1/v1* @param {String} name View to run list against

  • 8/3/2019 couch-js

    17/21

    * @param {ajaxSettings} options jQuery ajax settings*/

    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"

    );},

    /**

    * Fetch an arbitrary CouchDB database property* @see docs for /db/_prop* @param {String} propName Propery name to fetch

    * @param {ajaxSettings} options jQuery ajax settings* @param {ajaxSettings} ajaxOptions jQuery ajax settings

    */getDbProperty: function(propName, options, ajaxOptions) {

    ajax({url: this.uri + propName + encodeOptions(options)},options,"The property could not be retrieved",ajaxOptions

    );

    },

    /*** Set an arbitrary CouchDB database property* @see docs for /db/_prop* @param {String} propName Propery name to fetch* @param {String} propValue Propery value to set* @param {ajaxSettings} options jQuery ajax settings* @param {ajaxSettings} ajaxOptions jQuery ajax settings*/setDbProperty: function(propName, propValue, options, ajaxOptions) {

  • 8/3/2019 couch-js

    18/21

    ajax({type: "PUT",url: this.uri + propName + encodeOptions(options),

    data : JSON.stringify(propValue)},options,

    "The property could not be updated",ajaxOptions);

    }};

    },

    encodeDocId: encodeDocId,

    /*** Accessing the root of a CouchDB instance returns meta information about* the instance. The response is a JSON structure containing information* about the server, including a welcome message and the version of the

    * server.* @see * docs for GET /

    * @param {ajaxSettings} options jQuery ajax settings

    */info: function(options) {ajax({url: this.urlPrefix + "/"},

    options,"Server information could not be retrieved"

    );},

    /**

    * Request, configure, or stop, a replication operation.* @see docs for POST /_replicate

    * @param {String} source Path or url to source database

    * @param {String} target Path or url to target database* @param {ajaxSettings} ajaxOptions jQuery ajax settings* @param {Object} repOpts Additional replication options*/

    replicate: function(source, target, ajaxOptions, repOpts) {repOpts = $.extend({source: source, target: target}, repOpts);if (repOpts.continuous && !repOpts.cancel) {ajaxOptions.successStatus = 202;

    }ajax({

    type: "POST", url: this.urlPrefix + "/_replicate",data: JSON.stringify(repOpts),contentType: "application/json"

  • 8/3/2019 couch-js

    19/21

    },ajaxOptions,"Replication failed"

    );},

    /*** Fetch a new UUID* @see docs for /_uuids* @param {Int} cacheNum Number of uuids to keep cached for future use*/

    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();

    }

    });

    /*** @private

    */function ajax(obj, options, errorMessage, ajaxOptions) {

    var defaultAjaxOpts = {contentType: "application/json",headers:{"Accept": "application/json"}

    };

    options = $.extend({successStatus: 200}, options);ajaxOptions = $.extend(defaultAjaxOpts, 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) {

  • 8/3/2019 couch-js

    20/21

    try {var resp = $.parseJSON(req.responseText);

    } 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));}

    /*** @private*/

    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('Accept', 'application/json');xhr.setRequestHeader("X-Couch-Full-Commit", commit.toString());

    };}

    };

    /*** @private*/// 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];

  • 8/3/2019 couch-js

    21/21

    if ($.inArray(name, ["key", "startkey", "endkey"]) >= 0) {value = toJSON(value);

    }

    buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));}

    }

    return buf.length ? "?" + buf.join("&") : "";}

    /*** @private*/function toJSON(obj) {

    return obj !== null ? JSON.stringify(obj) : null;}

    })(jQuery);