From 01060528e2f94e0ae4374eb3746093a868b6a2f2 Mon Sep 17 00:00:00 2001 From: Christopher Lenz Date: Tue, 15 Apr 2008 00:07:08 +0000 Subject: Merged futon-async branch back into trunk. git-svn-id: https://svn.apache.org/repos/asf/incubator/couchdb/trunk@648074 13f79535-47bb-0310-9956-ffa450edef68 --- share/Makefile.am | 2 + share/www/browse/database.html | 22 +- share/www/browse/document.html | 9 +- share/www/browse/index.html | 2 +- share/www/couch_tests.html | 6 + share/www/image/progress.gif | Bin 0 -> 10819 bytes share/www/image/spinner.gif | Bin 10819 -> 1849 bytes share/www/index.html | 26 +- share/www/replicator.html | 52 +-- share/www/script/browse.js | 647 ++++++++++++++++++++++---------------- share/www/script/jquery.couch.js | 271 ++++++++++++++++ share/www/script/jquery.dialog.js | 15 +- share/www/style/layout.css | 11 +- 13 files changed, 730 insertions(+), 333 deletions(-) create mode 100644 share/www/image/progress.gif create mode 100644 share/www/script/jquery.couch.js diff --git a/share/Makefile.am b/share/Makefile.am index fd4eff0e..f1f44b05 100644 --- a/share/Makefile.am +++ b/share/Makefile.am @@ -39,6 +39,7 @@ nobase_dist_pkgdata_DATA = \ www/image/order-asc.gif \ www/image/order-desc.gif \ www/image/path.gif \ + www/image/progress.gif \ www/image/run-mini.gif \ www/image/run.gif \ www/image/running.gif \ @@ -58,6 +59,7 @@ nobase_dist_pkgdata_DATA = \ www/script/couch_tests.js \ www/script/jquery.js \ www/script/jquery.cookies.js \ + www/script/jquery.couch.js \ www/script/jquery.dialog.js \ www/script/jquery.resizer.js \ www/script/jquery.suggest.js \ diff --git a/share/www/browse/database.html b/share/www/browse/database.html index e52c3b8a..c685baad 100644 --- a/share/www/browse/database.html +++ b/share/www/browse/database.html @@ -21,20 +21,21 @@ specific language governing permissions and limitations under the License. + - + - + - - + diff --git a/share/www/replicator.html b/share/www/replicator.html index 36dc3d17..684826ce 100644 --- a/share/www/replicator.html +++ b/share/www/replicator.html @@ -20,7 +20,7 @@ specific language governing permissions and limitations under the License. - + diff --git a/share/www/script/browse.js b/share/www/script/browse.js index 8ae1e589..332b73d8 100644 --- a/share/www/script/browse.js +++ b/share/www/script/browse.js @@ -18,35 +18,50 @@ function CouchIndexPage() { this.addDatabase = function() { $.showDialog("_create_database.html", { - submit: function(data) { + submit: function(data, callback) { if (!data.name || data.name.length == 0) { - return {name: "Please enter a name."}; - } - try { - new CouchDB(data.name).createDb(); - } catch (e) { - return {name: e.reason}; + callback({name: "Please enter a name."}); + return; } - if (window !== parent) parent.setTimeout("updateDatabaseList()", 500); - window.open("database.html?" + data.name, "content"); + $.couch.db(data.name).create({ + error: function(status, id, reason) { callback({name: reason}) }, + success: function(resp) { + if (window !== parent) parent.setTimeout("updateDatabaseList()", 500); + location.href = "database.html?" + encodeURIComponent(data.name); + callback(); + } + }); } }); return false; } this.updateDatabaseListing = function() { - var allDbs = CouchDB.allDbs(); - for (var i = 0; i < allDbs.length; i++) { - var dbName = allDbs[i]; - var info = new CouchDB(dbName).info(); - $("#databases tbody.content").append( - "" + - dbName + "" + prettyPrintSize(info.disk_size) + - "" + info.doc_count + - "" + info.update_seq + ""); - $("#databases tbody tr:odd").addClass("odd"); - $("#databases tbody.footer tr td").text(allDbs.length + " database(s)"); - } + $(document.body).addClass("loading"); + $.couch.allDbs({ + success: function(dbs) { + $.each(dbs, function(idx, dbName) { + $("#databases tbody.content").append("" + + "" + + dbName + "" + + "" + + ""); + $.couch.db(dbName).info({ + success: function(info) { + $("#databases tbody.content tr:eq(" + idx + ")") + .find("td.size").text(prettyPrintSize(info.disk_size)).end() + .find("td.count").text(info.doc_count).end() + .find("td.seq").text(info.update_seq); + if (idx == dbs.length - 1) { + $(document.body).removeClass("loading"); + } + } + }); + }); + $("#databases tbody tr:odd").addClass("odd"); + $("#databases tbody.footer tr td").text(dbs.length + " database(s)"); + } + }); } } @@ -56,7 +71,7 @@ function CouchIndexPage() { */ function CouchDatabasePage() { var urlParts = location.search.substr(1).split("/"); - var dbName = urlParts.shift(); + var dbName = decodeURIComponent(urlParts.shift()); var viewName = (urlParts.length > 0) ? urlParts.join("/") : null; if (viewName) { viewName = decodeURIComponent(viewName); @@ -64,7 +79,7 @@ function CouchDatabasePage() { } else { viewName = $.cookies.get(dbName + ".view") || ""; } - var db = new CouchDB(dbName); + var db = $.couch.db(dbName); this.dbName = dbName; this.viewName = viewName; @@ -74,100 +89,138 @@ function CouchDatabasePage() { this.addDocument = function() { $.showDialog("_create_document.html", { - submit: function(data) { - try { - var result = db.save(data.docid ? {_id: data.docid} : {}); - } catch (err) { - return {docid: err.reason}; - } - location.href = "document.html?" + dbName + "/" + result.id; + submit: function(data, callback) { + db.saveDoc(data.docid ? {_id: data.docid} : {}, { + error: function(status, error, reason) { + callback({docid: reason}); + }, + success: function(resp) { + location.href = "document.html?" + encodeURIComponent(dbName) + + "/" + encodeURIComponent(resp.id); + } + }); } }); } this.compactDatabase = function() { $.showDialog("_compact_database.html", { - submit: function() { - db.compact(); + submit: function(data, callback) { + db.compact({ + success: function(resp) { + callback(); + } + }); } }); } this.deleteDatabase = function() { $.showDialog("_delete_database.html", { - submit: function() { - db.deleteDb(); - location.href = "index.html"; - if (window !== null) { - parent.$("#dbs li").filter(function(index) { - return $("a", this).text() == dbName; - }).remove(); - } + submit: function(data, callback) { + db.drop({ + success: function(resp) { + callback(); + location.href = "index.html"; + if (window !== null) { + parent.$("#dbs li").filter(function(index) { + return $("a", this).text() == dbName; + }).remove(); + } + } + }); } }); } this.populateViewEditor = function() { if (viewName.match(/^_design\//)) { - page.revertViewChanges(); - var dirtyTimeout = null; - function updateDirtyState() { - clearTimeout(dirtyTimeout); - dirtyTimeout = setTimeout(function() { - var buttons = $("#viewcode button.save, #viewcode button.revert"); - page.isDirty = $("#viewcode textarea").val() != page.storedViewCode; - if (page.isDirty) { - buttons.removeAttr("disabled"); - } else { - buttons.attr("disabled", "disabled"); - } - }, 100); - } - $("#viewcode textarea").bind("input", updateDirtyState); - if ($.browser.msie) { // sorry, browser detection - $("#viewcode textarea").get(0).onpropertychange = updateDirtyState - } else if ($.browser.safari) { - $("#viewcode textarea").bind("paste", updateDirtyState) - .bind("change", updateDirtyState) - .bind("keydown", updateDirtyState) - .bind("keypress", updateDirtyState) - .bind("keyup", updateDirtyState) - .bind("textInput", updateDirtyState); - } + page.revertViewChanges(function() { + var dirtyTimeout = null; + function updateDirtyState() { + clearTimeout(dirtyTimeout); + dirtyTimeout = setTimeout(function() { + var buttons = $("#viewcode button.save, #viewcode button.revert"); + page.isDirty = $("#viewcode textarea").val() != page.storedViewCode; + if (page.isDirty) { + buttons.removeAttr("disabled"); + } else { + buttons.attr("disabled", "disabled"); + } + }, 100); + } + $("#viewcode textarea").bind("input", updateDirtyState); + if ($.browser.msie) { // sorry, browser detection + $("#viewcode textarea").get(0).onpropertychange = updateDirtyState + } else if ($.browser.safari) { + $("#viewcode textarea").bind("paste", updateDirtyState) + .bind("change", updateDirtyState) + .bind("keydown", updateDirtyState) + .bind("keypress", updateDirtyState) + .bind("keyup", updateDirtyState) + .bind("textInput", updateDirtyState); + } + }); } } this.populateViewsMenu = function() { - var designDocs = db.allDocs({startkey: "_design/", endkey: "_design/ZZZ"}); - $("#switch select").each(function() { - this.options.length = 3; - for (var i = 0; i < designDocs.rows.length; i++) { - var doc = db.open(designDocs.rows[i].id); - var optGroup = $("").attr("label", doc._id.substr(8)); - for (var name in doc.views) { - if (!doc.views.hasOwnProperty(name)) continue; - $("").attr("value", doc._id + "/" + name).text(name) - .appendTo(optGroup); + var select = $("#switch select"); + db.allDocs({startkey: "_design/", endkey: "_design/ZZZ", + success: function(resp) { + select[0].options.length = 3; + for (var i = 0; i < resp.rows.length; i++) { + db.openDoc(resp.rows[i].id, { + success: function(doc) { + var optGroup = $("").attr("label", doc._id.substr(8)); + for (var name in doc.views) { + if (!doc.views.hasOwnProperty(name)) continue; + var option = $("").attr("value", doc._id + "/" + name).text(name) + .appendTo(optGroup); + if (doc._id + "/" + name == viewName) { + option[0].selected = true; + } + } + optGroup.appendTo(select); + } + }); } - optGroup.appendTo(this); } - this.autocomplete = false; }); + if (!viewName.match(/^_design\//)) { + $.each(["_all_docs", "_design_docs", "_temp_view"], function(idx, name) { + if (viewName == name) { + select[0].options[idx].selected = true; + } + }); + } } - this.revertViewChanges = function() { + this.revertViewChanges = function(callback) { if (!page.storedViewCode) { var viewNameParts = viewName.split("/"); var designDocId = viewNameParts[1]; var localViewName = viewNameParts[2]; - var designDoc = db.open(["_design", designDocId].join("/")); - if (designDoc) { - page.storedViewCode = designDoc.views[localViewName]; - } + db.openDoc(["_design", designDocId].join("/"), { + error: function(status, error, reason) { + if (status == 404) { + $.cookies.remove(dbName + ".view"); + location.reload(); + } + }, + success: function(resp) { + page.storedViewCode = resp.views[localViewName]; + $("#viewcode textarea").val(page.storedViewCode); + $("#viewcode button.revert, #viewcode button.save").attr("disabled", "disabled"); + if (callback) callback(); + } + }); + } else { + $("#viewcode textarea").val(page.storedViewCode); + page.isDirty = false; + $("#viewcode button.revert, #viewcode button.save").attr("disabled", "disabled"); + if (callback) callback(); } - $("#viewcode textarea").val(page.storedViewCode); - page.isDirty = false; - $("#viewcode button.revert, #viewcode button.save").attr("disabled", "disabled"); } this.saveViewAs = function() { @@ -181,49 +234,68 @@ function CouchDatabasePage() { $.showDialog("_save_view_as.html", { load: function(elem) { $("#input_docid", elem).val(designDocId).suggest(function(text, callback) { - var matches = []; - var docs = db.allDocs({ + db.allDocs({ count: 10, startkey: "_design/" + text, - endkey: "_design/" + text + "ZZZZ" + endkey: "_design/" + text + "ZZZZ", + success: function(docs) { + var matches = []; + for (var i = 0; i < docs.rows.length; i++) { + matches[i] = docs.rows[i].id.substr(8); + } + callback(matches); + } }); - for (var i = 0; i < docs.rows.length; i++) { - matches[i] = docs.rows[i].id.substr(8); - } - callback(matches); }); $("#input_name", elem).val(localViewName).suggest(function(text, callback) { - var matches = []; - try { - var doc = db.open("_design/" + $("#input_docid").val()); - } catch (err) { - return; - } - if (!doc || !doc.views) return; - for (var viewName in doc.views) { - if (!doc.views.hasOwnProperty(viewName) || !viewName.match("^" + text)) { - continue; + db.openDoc("_design/" + $("#input_docid").val(), { + error: function() {}, // ignore + success: function(doc) { + var matches = []; + if (!doc.views) return; + for (var viewName in doc.views) { + if (!doc.views.hasOwnProperty(viewName) || !viewName.match("^" + text)) { + continue; + } + matches.push(viewName); + } + callback(matches); } - matches.push(viewName); - } - callback(matches); + }); }); }, - submit: function(data) { + submit: function(data, callback) { if (!data.docid || !data.name) { var errors = {}; if (!data.docid) errors.docid = "Please enter a document ID"; if (!data.name) errors.name = "Please enter a view name"; - return errors; + callback(errors); + } else { + var viewCode = $("#viewcode textarea").val(); + var docId = ["_design", data.docid].join("/"); + function save(doc) { + if (!doc) doc = {_id: docId, language: "text/javascript"}; + if (doc.views === undefined) doc.views = {}; + doc.views[data.name] = viewCode; + db.saveDoc(doc, { + success: function(resp) { + callback(); + page.isDirty = false; + location.href = "database.html?" + encodeURIComponent(dbName) + + "/" + encodeURIComponent(doc._id) + + "/" + encodeURIComponent(data.name); + } + }); + } + db.openDoc(docId, { + error: function(status, error, reason) { + if (status == 404) save(null); + else alert(reason); + }, + success: function(doc) { + save(doc); + } + }); } - var viewCode = $("#viewcode textarea").val(); - var docId = ["_design", data.docid].join("/"); - var designDoc = db.open(docId); - if (!designDoc) designDoc = {_id: docId, language: "text/javascript"}; - if (designDoc.views === undefined) designDoc.views = {}; - designDoc.views[data.name] = viewCode; - db.save(designDoc); - page.isDirty = false; - location.href = "database.html?" + dbName + "/" + designDoc._id + "/" + data.name; } }); } @@ -232,24 +304,34 @@ function CouchDatabasePage() { var viewNameParts = viewName.split("/"); var designDocId = viewNameParts[1]; var localViewName = viewNameParts[2]; - var designDoc = db.open(["_design", designDocId].join("/")); - var viewCode = $("#viewcode textarea").val(); - designDoc.views[localViewName] = viewCode; - db.save(designDoc); - page.isDirty = false; - $("#viewcode button.revert, #viewcode button.save").attr("disabled", "disabled"); + $(document.body).addClass("loading"); + db.openDoc(["_design", designDocId].join("/"), { + success: function(doc) { + doc.views[localViewName] = $("#viewcode textarea").val(); + db.saveDoc(doc, { + success: function(resp) { + page.isDirty = false; + $("#viewcode button.revert, #viewcode button.save") + .attr("disabled", "disabled"); + $(document.body).removeClass("loading"); + } + }); + } + }); } this.updateDesignDocLink = function() { if (viewName && /^_design/.test(viewName)) { var docId = "_design/" + viewName.split("/")[1]; - $("#designdoc-link").attr("href", "document.html?" + dbName + "/" + docId).text(docId); + $("#designdoc-link").attr("href", "document.html?" + + encodeURIComponent(dbName) + "/" + encodeURIComponent(docId)).text(docId); } else { $("#designdoc-link").removeAttr("href").text(""); } } this.updateDocumentListing = function(options) { + $(document.body).addClass("loading"); if (options === undefined) options = {}; if (options.count === undefined) { options.count = parseInt($("#perpage").val(), 10); @@ -265,99 +347,92 @@ function CouchDatabasePage() { $("#documents tbody.content").empty(); this.updateDesignDocLink(); - var result = null; + function handleResults(resp) { + if (resp.offset === undefined) { + resp.offset = 0; + } + if (resp.offset > 0) { + $("#paging a.prev").attr("href", "#" + (resp.offset - options.count)).click(function() { + var firstDoc = resp.rows[0]; + page.updateDocumentListing({ + startkey: firstDoc.key !== undefined ? firstDoc.key : null, + startkey_docid: firstDoc.id, + skip: 1, + count: -options.count + }); + return false; + }); + } else { + $("#paging a.prev").removeAttr("href"); + } + if (resp.total_rows - resp.offset > options.count) { + $("#paging a.next").attr("href", "#" + (resp.offset + options.count)).click(function() { + var lastDoc = resp.rows[resp.rows.length - 1]; + page.updateDocumentListing({ + startkey: lastDoc.key !== undefined ? lastDoc.key : null, + startkey_docid: lastDoc.id, + skip: 1, + count: options.count + }); + return false; + }); + } else { + $("#paging a.next").removeAttr("href"); + } + + for (var i = 0; i < resp.rows.length; i++) { + var row = resp.rows[i]; + var tr = $(""); + var key = row.key; + $("
" + + "ID: " + row.id + "
") + .find("em").text(key !== null ? prettyPrintJSON(key, 0, "") : "null").end() + .appendTo(tr); + var value = row.value; + $("").text( + value !== null ? prettyPrintJSON(value, 0, "") : "null" + ).appendTo(tr).dblclick(function() { + location.href = this.previousSibling.firstChild.href; + }); + tr.appendTo("#documents tbody.content"); + } + + $("#documents tbody tr:odd").addClass("odd"); + $("#documents tbody.footer td span").text( + "Showing " + Math.min(resp.total_rows, resp.offset + 1) + "-" + + (resp.offset + resp.rows.length) + " of " + resp.total_rows + + " document" + (resp.total_rows != 1 ? "s" : "")); + $(document.body).removeClass("loading"); + } + options.success = handleResults; + options.error = function(error, reason) { + alert(reason); + } + if (!viewName) { $("#switch select").get(0).selectedIndex = 0; - result = db.allDocs(options); + db.allDocs(options); } else { - $("#switch select").each(function() { - for (var i = 0; i < this.options.length; i++) { - if (this.options[i].value == viewName) { - this.selectedIndex = i; - break; - } - } - }); - docs = []; if (viewName == "_temp_view") { $("#viewcode").show().addClass("expanded"); var query = $("#viewcode textarea").val(); $.cookies.set(db.name + ".query", query); - try { - result = db.query(query, options); - } catch (e) { - alert(e.reason ? e.reason : e.message); - return; - } + db.query(query, options); } else if (viewName == "_design_docs") { options.startkey = options.descending ? "_design/ZZZZ" : "_design/"; options.endkey = options.descending ? "_design/" : "_design/ZZZZ"; - result = db.allDocs(options); + db.allDocs(options); } else { $("#viewcode").show(); var currentViewCode = $("#viewcode textarea").val(); - if (currentViewCode != page.storedViewCode) { - result = db.query(currentViewCode, options); + if (page.isDirty) { + db.query(currentViewCode, options); } else { - result = db.view(viewName.substr(8), options); + db.view(viewName.substr(8), options); } } } - if (result.offset === undefined) { - result.offset = 0; - } - if (result.offset > 0) { - $("#paging a.prev").attr("href", "#" + (result.offset - options.count)).click(function() { - var firstDoc = result.rows[0]; - page.updateDocumentListing({ - startkey: firstDoc.key !== undefined ? firstDoc.key : null, - startkey_docid: firstDoc.id, - skip: 1, - count: -options.count - }); - return false; - }); - } else { - $("#paging a.prev").removeAttr("href"); - } - if (result.total_rows - result.offset > options.count) { - $("#paging a.next").attr("href", "#" + (result.offset + options.count)).click(function() { - var lastDoc = result.rows[result.rows.length - 1]; - page.updateDocumentListing({ - startkey: lastDoc.key !== undefined ? lastDoc.key : null, - startkey_docid: lastDoc.id, - skip: 1, - count: options.count - }); - return false; - }); - } else { - $("#paging a.next").removeAttr("href"); - } - - for (var i = 0; i < result.rows.length; i++) { - var row = result.rows[i]; - var tr = $(""); - var key = row.key; - $("" + - "
ID: " + row.id + - "
").find("em").text( - key !== null ? prettyPrintJSON(key, 0, "") : "null" - ).end().appendTo(tr); - var value = row.value; - $("").text( - value !== null ? prettyPrintJSON(value, 0, "") : "null" - ).appendTo(tr).dblclick(function() { - location.href = this.previousSibling.firstChild.href; - }); - tr.appendTo("#documents tbody.content"); - } - - $("#documents tbody tr:odd").addClass("odd"); - $("#documents tbody.footer td span").text( - "Showing " + Math.min(result.total_rows, result.offset + 1) + "-" + - (result.offset + result.rows.length) + " of " + result.total_rows + - " document" + (result.total_rows != 1 ? "s" : "")); } window.onbeforeunload = function() { @@ -375,103 +450,125 @@ function CouchDatabasePage() { */ function CouchDocumentPage() { var urlParts = location.search.substr(1).split("/"); - var dbName = urlParts.shift(); + var dbName = decodeURIComponent(urlParts.shift()); var idParts = urlParts.join("/").split("@", 2); var docId = decodeURIComponent(idParts[0]); var docRev = (idParts.length > 1) ? idParts[1] : null; - var db = new CouchDB(dbName); - var doc = db.open(docId, {revs_info: true}); - var revs = doc._revs_info; - delete doc._revs_info; - if (docRev != null) { - try { - doc = db.open(docId, {rev: docRev}); - } catch (e) { - alert("The requested revision was not found. " + - "You will be redirected back to the latest revision."); - location.href = "?" + dbName + "/" + docId; - return; - } - } + var db = $.couch.db(dbName); this.dbName = dbName; this.db = db; - this.doc = doc; + this.docId = docId; + this.doc = null; this.isDirty = false; page = this; this.addField = function() { var fieldName = "unnamed"; var fieldIdx = 1; - while (doc.hasOwnProperty(fieldName)) { + while (page.doc.hasOwnProperty(fieldName)) { fieldName = "unnamed " + fieldIdx++; } - doc[fieldName] = null; - var row = _addRowForField(fieldName); + page.doc[fieldName] = null; + var row = _addRowForField(page.doc, fieldName); page.isDirty = true; - _editKey(row.find("th"), fieldName); + _editKey(page.doc, row.find("th"), fieldName); } this.updateFieldListing = function() { + $(document.body).addClass("loading"); $("#fields tbody.content").empty(); - var propNames = []; - for (var prop in doc) { - if (!doc.hasOwnProperty(prop)) continue; - propNames.push(prop); - } - // Order properties alphabetically, but put internal fields first - propNames.sort(function(a, b) { - var a0 = a.charAt(0), b0 = b.charAt(0); - if (a0 == "_" && b0 != "_") { - return -1; - } else if (a0 != "_" && b0 == "_") { - return 1; - } else { - return a < b ? -1 : a != b ? 1 : 0; + + function handleResult(doc, revs) { + page.doc = doc; + var propNames = []; + for (var prop in doc) { + if (!doc.hasOwnProperty(prop)) continue; + propNames.push(prop); } - }); - for (var pi = 0; pi < propNames.length; pi++) { - _addRowForField(propNames[pi]); - } - if (revs.length > 1) { - var currentIndex = 0; - for (var i = 0; i < revs.length; i++) { - if (revs[i].rev == doc._rev) { - currentIndex = i; - break; + // Order properties alphabetically, but put internal fields first + propNames.sort(function(a, b) { + var a0 = a.charAt(0), b0 = b.charAt(0); + if (a0 == "_" && b0 != "_") { + return -1; + } else if (a0 != "_" && b0 == "_") { + return 1; + } else { + return a < b ? -1 : a != b ? 1 : 0; } + }); + for (var pi = 0; pi < propNames.length; pi++) { + _addRowForField(doc, propNames[pi]); } - if (currentIndex < revs.length - 1) { - var prevRev = revs[currentIndex + 1].rev; - $("#paging a.prev").attr("href", "?" + dbName + "/" + docId + "@" + prevRev); - } - if (currentIndex > 0) { - var nextRev = revs[currentIndex - 1].rev; - $("#paging a.next").attr("href", "?" + dbName + "/" + docId + "@" + nextRev); + if (revs.length > 1) { + var currentIndex = 0; + for (var i = 0; i < revs.length; i++) { + if (revs[i].rev == doc._rev) { + currentIndex = i; + break; + } + } + if (currentIndex < revs.length - 1) { + var prevRev = revs[currentIndex + 1].rev; + $("#paging a.prev").attr("href", "?" + encodeURIComponent(dbName) + + "/" + encodeURIComponent(docId) + "@" + prevRev); + } + if (currentIndex > 0) { + var nextRev = revs[currentIndex - 1].rev; + $("#paging a.next").attr("href", "?" + encodeURIComponent(dbName) + + "/" + encodeURIComponent(docId) + "@" + nextRev); + } + $("#fields tbody.footer td span").text("Showing revision " + + (revs.length - currentIndex) + " of " + revs.length); } - $("#fields tbody.footer td span").text("Showing revision " + - (revs.length - currentIndex) + " of " + revs.length); + $(document.body).removeClass("loading"); } + + db.openDoc(docId, {revs_info: true, + success: function(doc) { + var revs = doc._revs_info; + delete doc._revs_info; + if (docRev != null) { + db.openDoc(docId, {rev: docRev, + error: function(status, error, reason) { + alert("The requested revision was not found. " + + "You will be redirected back to the latest revision."); + location.href = "?" + encodeURIComponent(dbName) + + "/" + encodeURIComponent(docId); + }, + success: function(doc) { + handleResult(doc, revs); + } + }); + } else { + handleResult(doc, revs); + } + } + }); } this.deleteDocument = function() { $.showDialog("_delete_document.html", { - submit: function() { - db.deleteDoc(doc); - location.href = "database.html?" + dbName; + submit: function(data, callback) { + db.removeDoc(page.doc, { + success: function(resp) { + callback(); + location.href = "database.html?" + encodeURIComponent(dbName); + } + }); } }); } this.saveDocument = function() { - try { - db.save(doc); - } catch (e) { - alert(e.reason); - return; - } - page.isDirty = false; - location.href = "?" + dbName + "/" + docId; + $(document.body).addClass("loading"); + db.saveDoc(page.doc, { + success: function(resp) { + page.isDirty = false; + location.href = "?" + encodeURIComponent(dbName) + + "/" + encodeURIComponent(docId); + } + }); } window.onbeforeunload = function() { @@ -481,26 +578,26 @@ function CouchDocumentPage() { } } - function _addRowForField(fieldName) { + function _addRowForField(doc, fieldName) { var value = _renderValue(doc[fieldName]); var row = $("") .find("th").append($("").text(fieldName)).dblclick(function() { - _editKey(this, $(this).text()); + _editKey(doc, this, $(this).text()); }).end() .find("td").append(value).dblclick(function() { - _editValue(this, $(this).prev("th").text()); + _editValue(doc, this, $(this).prev("th").text()); }).end() .appendTo("#fields tbody.content"); if (fieldName != "_id" && fieldName != "_rev") { row.find("th, td").attr("title", "Double click to edit"); - _initKey(row, fieldName); + _initKey(doc, row, fieldName); _initValue(value); } $("#fields tbody tr").removeClass("odd").filter(":odd").addClass("odd"); return row; } - function _editKey(cell, fieldName) { + function _editKey(doc, cell, fieldName) { if (fieldName == "_id" || fieldName == "_rev") return; var th = $(cell); th.empty(); @@ -523,13 +620,13 @@ function CouchDocumentPage() { delete doc[fieldName]; th.children().remove(); th.append($("").text(newName)); - _initKey(th.parent("tr"), fieldName); + _initKey(doc, th.parent("tr"), fieldName); page.isDirty = true; } function cancelChange() { th.children().remove(); th.append($("").text(fieldName)); - _initKey(th.parent("tr"), fieldName); + _initKey(doc, th.parent("tr"), fieldName); } $("").click(function() { @@ -543,8 +640,8 @@ function CouchDocumentPage() { input.each(function() { this.focus(); this.select(); }); } - function _editValue(cell, fieldName) { - if (fieldName == "_id" || fieldName == "_rev") return; + function _editValue(doc, cell, fieldName) { + if (!fieldName || fieldName == "_id" || fieldName == "_rev") return; var td = $(cell); var value = doc[fieldName]; var needsTextarea = $("dl", td).length > 0 || $("code", td).text().length > 60; @@ -591,10 +688,10 @@ function CouchDocumentPage() { _initValue(value); } - $("").click(function() { + $("").click(function() { applyChange(); }).appendTo(tools); - $("").click(function() { + $("").click(function() { cancelChange(); }).appendTo(tools); tools.appendTo(td); @@ -603,7 +700,7 @@ function CouchDocumentPage() { if (needsTextarea) input.resizable(); } - function _initKey(row, fieldName) { + function _initKey(doc, row, fieldName) { if (fieldName != "_id" && fieldName != "_rev") { $("").click(function() { delete doc[fieldName]; diff --git a/share/www/script/jquery.couch.js b/share/www/script/jquery.couch.js new file mode 100644 index 00000000..de1cddc9 --- /dev/null +++ b/share/www/script/jquery.couch.js @@ -0,0 +1,271 @@ +// 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 || {} + $.fn.extend($.couch, { + + allDbs: function(options) { + $.ajax({ + type: "GET", url: "/_all_dbs", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("An error occurred retrieving the list of all databases: " + + resp.reason); + } + } + }); + }, + + db: function(name) { + return { + name: name, + uri: "/" + encodeURIComponent(name) + "/", + + compact: function(options) { + $.ajax({ + type: "POST", url: this.uri + "_compact", dataType: "json", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 202 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("The database could not be compacted: " + resp.reason); + } + } + }); + }, + create: function(options) { + $.ajax({ + type: "PUT", url: this.uri, dataType: "json", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 201 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("The database could not be created: " + resp.reason); + } + } + }); + }, + drop: function(options) { + $.ajax({ + type: "DELETE", url: this.uri, dataType: "json", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 202 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("The database could not be deleted: " + resp.reason); + } + } + }); + }, + info: function(options) { + $.ajax({ + type: "GET", url: this.uri, dataType: "json", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("Database information could not be retrieved: " + + resp.reason); + } + } + }); + }, + allDocs: function(options) { + $.ajax({ + type: "GET", url: this.uri + "_all_docs" + encodeOptions(options), + dataType: "json", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("An error occurred retrieving a list of all documents: " + + resp.reason); + } + }, + }); + }, + openDoc: function(docId, options) { + $.ajax({ + type: "GET", + url: this.uri + encodeURIComponent(docId) + encodeOptions(options), + dataType: "json", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("The document could not be retrieved: " + resp.reason); + } + } + }); + }, + saveDoc: function(doc, options) { + if (doc._id === undefined) { + var method = "POST"; + var uri = this.uri; + } else { + var method = "PUT"; + var uri = this.uri + encodeURIComponent(doc._id); + } + $.ajax({ + type: method, url: uri + encodeOptions(options), + dataType: "json", data: toJSON(doc), + contentType: "application/json", + complete: function(req) { + var resp = $.httpData(req, "json") + doc._id = resp.id; + doc._rev = resp.rev; + if (req.status == 201 && 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); + } + } + }); + }, + removeDoc: function(doc, options) { + $.ajax({ + type: "DELETE", + url: this.uri + encodeURIComponent(doc._id) + encodeOptions({rev: doc._rev}), + dataType: "json", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 202 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("The document could not be deleted: " + resp.reason); + } + } + }); + }, + query: function(fun, options) { + if (typeof(fun) != "string") + fun = fun.toSource ? fun.toSource() : "(" + fun.toString() + ")"; + $.ajax({ + type: "POST", url: this.uri + "_temp_view" + encodeOptions(options), + contentType: "text/javascript", data: fun, dataType: "json", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("An error occurred querying the database: " + resp.reason); + } + } + }); + }, + view: function(name, options) { + $.ajax({ + type: "GET", url: this.uri + "_view/" + name + encodeOptions(options), + dataType: "json", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("An error occurred accessing the view: " + resp.reason); + } + } + }); + } + }; + }, + + info: function(options) { + $.ajax({ + type: "GET", url: "/", dataType: "json", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("Server information could not be retrieved: " + resp.reason); + } + } + }); + }, + + replicate: function(source, target, options) { + $.ajax({ + type: "POST", url: "/_replicate", dataType: "json", + data: JSON.stringify({source: source, target: target}), + contentType: "application/json", + complete: function(req) { + var resp = $.httpData(req, "json"); + if (req.status == 200 && options.success) { + options.success(resp); + } else if (options.error) { + options.error(req.status, resp.error, resp.reason); + } else { + alert("Replication failed: " + resp.reason); + } + } + }); + } + + }); + + // 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 (name == "error" || name == "success") continue; + var value = options[name]; + if (name == "key" || name == "startkey" || name == "endkey") { + 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); diff --git a/share/www/script/jquery.dialog.js b/share/www/script/jquery.dialog.js index 09902758..1fd3d572 100644 --- a/share/www/script/jquery.dialog.js +++ b/share/www/script/jquery.dialog.js @@ -75,14 +75,15 @@ $.each($("form :input", dialog).serializeArray(), function(i, field) { data[field.name] = field.value; }); - var errors = options.submit(data); - if (errors == null || errors == {}) { - dismiss(); - } else { - for (var name in errors) { - showError(name, errors[name]); + options.submit(data, function callback(errors) { + if (errors == null || errors == {}) { + dismiss(); + } else { + for (var name in errors) { + showError(name, errors[name]); + } } - } + }); return false; }); }); diff --git a/share/www/style/layout.css b/share/www/style/layout.css index 32bbb7e8..62f64587 100644 --- a/share/www/style/layout.css +++ b/share/www/style/layout.css @@ -33,7 +33,10 @@ h1 :link, h1 :visited { h1 :link, h1 :visited { color: #bbb; cursor: pointer; text-shadow: #333 2px 2px 1px; } -h1 strong { color: #fff; text-shadow: #000 2px 2px 4px; } +h1 strong { color: #fff; padding-right: 25px; text-shadow: #000 2px 2px 4px; } +body.loading h1 strong { + background: url(../image/spinner.gif) right center no-repeat; +} hr { border: 1px solid #999; border-width: 1px 0 0; } dl dt { font-weight: bold; } @@ -49,6 +52,8 @@ fieldset legend { color: #666; font-weight: bold; padding: 0; } fieldset input, fieldset select { font-size: 95%; } fieldset p { margin: .4em; } +p.help { color: #999; font-size: 90%; margin: 0 2em 1em; } + /* Tabular listings */ table.listing { border-collapse: separate; border-spacing: 0; @@ -195,7 +200,7 @@ ul.suggest-dropdown li.selected { cursor: pointer; background: Highlight; width: expression(document.body.clientWidth + 'px'); height: expression(document.body.clientHeight + 'px'); } -#dialog { background: #333 url(../image/spinner.gif) 50% 50% no-repeat; +#dialog { background: #333 url(../image/progress.gif) 50% 50% no-repeat; color: #f4f4f4; overflow: hidden; opacity: .95; max-width: 33em; padding: 1em 1em 0; -moz-border-radius: 7px; -webkit-border-radius: 7px; -webkit-box-shadow: 4px 4px 6px #333; @@ -213,7 +218,7 @@ ul.suggest-dropdown li.selected { cursor: pointer; background: Highlight; -webkit-border-bottom-left-radius: 7px; -webkit-border-bottom-right-radius: 7px; } -#dialog p.help { color: #bbb; margin: 0 0 1em; } +#dialog p.help { color: #bbb; font-size: 95%; margin: 0 0 1em; } #dialog fieldset table { margin-top: 1em; } #dialog fieldset th, #dialog fieldset td { padding: .5em; vertical-align: top; -- cgit v1.2.3