diff options
Diffstat (limited to 'share/www')
-rw-r--r-- | share/www/browse/database.html | 22 | ||||
-rw-r--r-- | share/www/browse/document.html | 9 | ||||
-rw-r--r-- | share/www/browse/index.html | 2 | ||||
-rw-r--r-- | share/www/couch_tests.html | 6 | ||||
-rw-r--r-- | share/www/image/progress.gif | bin | 0 -> 10819 bytes | |||
-rw-r--r-- | share/www/image/spinner.gif | bin | 10819 -> 1849 bytes | |||
-rw-r--r-- | share/www/index.html | 26 | ||||
-rw-r--r-- | share/www/replicator.html | 52 | ||||
-rw-r--r-- | share/www/script/browse.js | 647 | ||||
-rw-r--r-- | share/www/script/jquery.couch.js | 271 | ||||
-rw-r--r-- | share/www/script/jquery.dialog.js | 15 | ||||
-rw-r--r-- | share/www/style/layout.css | 11 |
12 files changed, 728 insertions, 333 deletions
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. <script src="../script/json2.js"></script> <script src="../script/jquery.js"></script> <script src="../script/jquery.cookies.js"></script> + <script src="../script/jquery.couch.js"></script> <script src="../script/jquery.dialog.js"></script> <script src="../script/jquery.resizer.js"></script> <script src="../script/jquery.suggest.js"></script> - <script src="../script/couch.js"></script> <script src="../script/browse.js"></script> <script src="../script/pprint.js"></script> <script> var page = new CouchDatabasePage(); if (window != parent) { - parent.updateNavigation(location.pathname, location.search.split("/", 2)[0]); + parent.updateNavigation(location.pathname, + encodeURIComponent(location.search.split("/", 2)[0])); } $(document).ready(function() { - $("h1 strong").text(page.dbName); + $("h1 strong").text(page.db.name); $("#viewcode label").click(function() { $("#viewcode").toggleClass("expanded"); }); @@ -53,11 +54,11 @@ specific language governing permissions and limitations under the License. $("#viewcode textarea").resizable({grippie: $("#viewcode .bottom")}); // Restore preferences/state from cookies - var query = $.cookies.get(page.dbName + ".query"); + var query = $.cookies.get(page.db.name + ".query"); if (query) $("#viewcode textarea").val(query); - var desc = $.cookies.get(page.dbName + ".desc"); + var desc = $.cookies.get(page.db.name + ".desc"); if (desc) $("#documents thead th.key").addClass("desc"); - var rowsPerPage = $.cookies.get(page.dbName + ".perpage"); + var rowsPerPage = $.cookies.get(page.db.name + ".perpage"); if (rowsPerPage) $("#perpage").val(rowsPerPage); page.populateViewsMenu(); @@ -66,8 +67,9 @@ specific language governing permissions and limitations under the License. $("#switch select").change(function() { var viewName = $(this).val(); - if (!viewName) $.cookies.remove(page.dbName + ".view"); - location.href = "?" + page.dbName + (viewName ? "/" + viewName : ""); + if (!viewName) $.cookies.remove(page.db.name + ".view"); + location.href = "?" + encodeURIComponent(page.db.name) + + (viewName ? "/" + encodeURIComponent(viewName) : ""); }); $("#documents thead th.key").click(function() { $(this).toggleClass("desc"); @@ -75,7 +77,7 @@ specific language governing permissions and limitations under the License. }); $("#perpage").change(function() { page.updateDocumentListing(); - $.cookies.set(page.dbName + ".perpage", this.value); + $.cookies.set(page.db.name + ".perpage", this.value); }); $("#toolbar button.add").click(page.addDocument); $("#toolbar button.compact").click(page.compactDatabase); @@ -91,7 +93,7 @@ specific language governing permissions and limitations under the License. </h1> <div id="wrap"> <div id="switch"> - <label>Select view: <select> + <label>Select view: <select autocomplete="false"> <option value="">All documents</option> <option value="_design_docs">Design documents</option> <option value="_temp_view">Custom query…</option> diff --git a/share/www/browse/document.html b/share/www/browse/document.html index f7876539..db3de935 100644 --- a/share/www/browse/document.html +++ b/share/www/browse/document.html @@ -21,9 +21,9 @@ specific language governing permissions and limitations under the License. <script src="../script/json2.js"></script> <script src="../script/jquery.js"></script> <script src="../script/jquery.cookies.js"></script> + <script src="../script/jquery.couch.js"></script> <script src="../script/jquery.dialog.js"></script> <script src="../script/jquery.resizer.js"></script> - <script src="../script/couch.js"></script> <script src="../script/browse.js"></script> <script src="../script/pprint.js"></script> <script> @@ -32,13 +32,14 @@ specific language governing permissions and limitations under the License. var dbLink = $("h1 a.dbname").get(0); parent.updateNavigation( location.pathname.replace(/document\.html/, "database.html"), - location.search.split("/")[0] + encodeURIComponent(location.search.split("/")[0]) ); } $(function() { - $("h1 a.dbname").text(page.dbName).attr("href", "database.html?" + page.dbName); - $("h1 strong").text(page.doc._id); + $("h1 a.dbname").text(page.dbName) + .attr("href", "database.html?" + encodeURIComponent(page.db.name)); + $("h1 strong").text(page.docId); page.updateFieldListing(); $("#toolbar button.save").click(page.saveDocument); $("#toolbar button.add").click(page.addField); diff --git a/share/www/browse/index.html b/share/www/browse/index.html index 63ef62e6..1d583825 100644 --- a/share/www/browse/index.html +++ b/share/www/browse/index.html @@ -21,8 +21,8 @@ specific language governing permissions and limitations under the License. <script src="../script/json2.js"></script> <script src="../script/jquery.js"></script> <script src="../script/jquery.cookies.js"></script> + <script src="../script/jquery.couch.js"></script> <script src="../script/jquery.dialog.js"></script> - <script src="../script/couch.js"></script> <script src="../script/browse.js"></script> <script src="../script/pprint.js"></script> <script> diff --git a/share/www/couch_tests.html b/share/www/couch_tests.html index a1e4ee6e..83a47771 100644 --- a/share/www/couch_tests.html +++ b/share/www/couch_tests.html @@ -48,6 +48,12 @@ specific language governing permissions and limitations under the License. <li><button class="run">Run All</button></li> <li><button class="load">Reload</button></li> </ul> + <p class="help"> + <strong>Note:</strong> Each of the tests will block the browser. If the + connection to your CouchDB server is slow, running the tests will take + some time, and you'll not be able to do much with your browser while + a test is being executed. + </p> <table class="listing" id="tests" cellspacing="0"> <caption>Tests</caption> <thead> diff --git a/share/www/image/progress.gif b/share/www/image/progress.gif Binary files differnew file mode 100644 index 00000000..d84f6537 --- /dev/null +++ b/share/www/image/progress.gif diff --git a/share/www/image/spinner.gif b/share/www/image/spinner.gif Binary files differindex d84f6537..37f782dd 100644 --- a/share/www/image/spinner.gif +++ b/share/www/image/spinner.gif diff --git a/share/www/index.html b/share/www/index.html index 8616989a..f74e672d 100644 --- a/share/www/index.html +++ b/share/www/index.html @@ -28,16 +28,20 @@ specific language governing permissions and limitations under the License. </style> <script src="script/json2.js"></script> <script src="script/jquery.js"></script> - <script src="script/couch.js"></script> + <script src="script/jquery.couch.js"></script> <script> function updateDatabaseList() { - var dbs = CouchDB.allDbs(); - $("#dbs").empty(); - for (var i = 0; i < dbs.length; i++) { - var dbname = dbs[i]; - $("#dbs").append("<li><a href='browse/database.html?" + dbname + - "' target='content'>" + dbname + "</a></li>"); - } + var list = $("#dbs").empty(); + var dbs = $.couch.allDbs({ + success: function(dbs, status) { + for (var i = 0; i < dbs.length; i++) { + var dbName = dbs[i]; + list.append("<li><a href='browse/database.html?" + + encodeURIComponent(dbName) + "' target='content'>" + dbName + + "</a></li>"); + } + } + }); } function updateNavigation(path, queryString) { function fixupPath(path) { // hack for IE/Win @@ -67,7 +71,11 @@ specific language governing permissions and limitations under the License. }); } $(function() { - $("#version").text(CouchDB.getVersion()); + $.couch.info({ + success: function(info, status) { + $("#version").text(info.version); + } + }); updateDatabaseList(); }); </script> 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. <link rel="stylesheet" href="style/layout.css" type="text/css"> <script src="script/json2.js"></script> <script src="script/jquery.js"></script> - <script src="script/couch.js"></script> + <script src="script/jquery.couch.js"></script> <script src="script/pprint.js"></script> <script> $(document).ready(function() { @@ -39,12 +39,17 @@ specific language governing permissions and limitations under the License. }); }); - var allDbs = CouchDB.allDbs(); - $("fieldset select").each(function() { - for (var i = 0; i < allDbs.length; i++) { - $("<option></option>").text(allDbs[i]).appendTo(this); + + $.couch.allDbs({ + success: function(dbs) { + $("fieldset select").each(function() { + var select = this; + $.each(dbs, function(idx, dbName) { + $("<option></option>").text(dbName).appendTo(select); + }); + select.selectedIndex = 0; + }); } - this.selectedIndex = 0; }); $("button#swap").click(function() { @@ -73,24 +78,23 @@ specific language governing permissions and limitations under the License. $("#records tbody.content").empty(); var source = $("#from_local")[0].checked ? $("#from_name").val() : $("#from_url").val(); var target = $("#to_local")[0].checked ? $("#to_name").val() : $("#to_url").val(); - try { - var results = CouchDB.replicate(source, target); - } catch (e) { - alert(e.reason); - return; - } - for (var i = 0; i < results.history.length; i++) { - var record = results.history[i]; - $("<tr><th></th><td class='seq'></td>" + - "<td class='read'></td><td class='copied'></td></tr>") - .find("th").text(record.start_time).end() - .find("td.seq").text(record.start_last_seq + "–" + record.end_last_seq).end() - .find("td.read").text(record.docs_read + " (" + record.read_errors + " errors)").end() - .find("td.copied").text(record.docs_copied + " (" + record.copy_errors + " errors)").end() - .appendTo("#records tbody.content"); - } - $("#records tbody tr").removeClass("odd").filter(":odd").addClass("odd"); - $("#records tbody.footer td").text("Replication session " + results.session_id); + $(document.body).addClass("loading"); + $.couch.replicate(source, target, { + success: function(resp) { + $.each(resp.history, function(idx, record) { + $("<tr><th></th><td class='seq'></td>" + + "<td class='read'></td><td class='copied'></td></tr>") + .find("th").text(record.start_time).end() + .find("td.seq").text(record.start_last_seq + "–" + record.end_last_seq).end() + .find("td.read").text(record.docs_read + " (" + record.read_errors + " errors)").end() + .find("td.copied").text(record.docs_copied + " (" + record.copy_errors + " errors)").end() + .appendTo("#records tbody.content"); + }); + $("#records tbody tr").removeClass("odd").filter(":odd").addClass("odd"); + $("#records tbody.footer td").text("Replication session " + resp.session_id); + $(document.body).removeClass("loading"); + } + }); }); }); </script> 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( - "<tr><th><a href='database.html?" + dbName + "'>" + - dbName + "</a></th><td class='size'>" + prettyPrintSize(info.disk_size) + - "</td><td class='count'>" + info.doc_count + - "</td><td class='seq'>" + info.update_seq + "</td></tr>"); - $("#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("<tr>" + + "<th><a href='database.html?" + encodeURIComponent(dbName) + "'>" + + dbName + "</a></th>" + + "<td class='size'></td><td class='count'></td>" + + "<td class='seq'></td></tr>"); + $.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 = $("<optgroup></optgroup>").attr("label", doc._id.substr(8)); - for (var name in doc.views) { - if (!doc.views.hasOwnProperty(name)) continue; - $("<option></option>").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 = $("<optgroup></optgroup>").attr("label", doc._id.substr(8)); + for (var name in doc.views) { + if (!doc.views.hasOwnProperty(name)) continue; + var option = $("<option></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 = $("<tr></tr>"); + var key = row.key; + $("<td class='key'><a href='document.html?" + encodeURIComponent(db.name) + + "/" + encodeURIComponent(row.id) + "'><em></em><br>" + + "<span class='docid'>ID: " + row.id + "</span></a></td>") + .find("em").text(key !== null ? prettyPrintJSON(key, 0, "") : "null").end() + .appendTo(tr); + var value = row.value; + $("<td class='value'></td>").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 = $("<tr></tr>"); - var key = row.key; - $("<td class='key'><a href='document.html?" + db.name + "/" + row.id + "'>" + - "<em></em><br><span class='docid'>ID: " + row.id + - "</span></a></td>").find("em").text( - key !== null ? prettyPrintJSON(key, 0, "") : "null" - ).end().appendTo(tr); - var value = row.value; - $("<td class='value'></td>").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 = $("<tr><th></th><td></td></tr>") .find("th").append($("<b></b>").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($("<b></b>").text(newName)); - _initKey(th.parent("tr"), fieldName); + _initKey(doc, th.parent("tr"), fieldName); page.isDirty = true; } function cancelChange() { th.children().remove(); th.append($("<b></b>").text(fieldName)); - _initKey(th.parent("tr"), fieldName); + _initKey(doc, th.parent("tr"), fieldName); } $("<button type='button' class='apply'></button>").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); } - $("<button type='button' class='apply'></button>").click(function() { + $("<button type='button' class='apply' title='Apply change'></button>").click(function() { applyChange(); }).appendTo(tools); - $("<button type='button' class='cancel'></button>").click(function() { + $("<button type='button' class='cancel' title='Revert change'></button>").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") { $("<button type='button' class='delete' title='Delete field'></button>").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; |