summaryrefslogtreecommitdiff
path: root/share/www/script/futon.browse.js
diff options
context:
space:
mode:
Diffstat (limited to 'share/www/script/futon.browse.js')
-rw-r--r--share/www/script/futon.browse.js199
1 files changed, 151 insertions, 48 deletions
diff --git a/share/www/script/futon.browse.js b/share/www/script/futon.browse.js
index 4d06d283..17975de2 100644
--- a/share/www/script/futon.browse.js
+++ b/share/www/script/futon.browse.js
@@ -62,7 +62,8 @@
.find("td.size").text($.futon.formatSize(info.disk_size)).end()
.find("td.count").text(info.doc_count).end()
.find("td.seq").text(info.update_seq);
- }
+ },
+ error : function() {}
});
});
$("#databases tbody tr:odd").addClass("odd");
@@ -96,7 +97,10 @@
// Page class for browse/database.html
CouchDatabasePage: function() {
var urlParts = location.search.substr(1).split("/");
- var dbName = decodeURIComponent(urlParts.shift());
+ var dbName = decodeURIComponent(urlParts.shift())
+
+ var dbNameRegExp = new RegExp("[^a-z0-9\_\$\(\)\+\/\-]", "g");
+ dbName = dbName.replace(dbNameRegExp, "");
$.futon.storage.declareWithPrefix(dbName + ".", {
desc: {},
@@ -106,7 +110,8 @@
reduce: {},
group_level: {defaultValue: 100},
per_page: {defaultValue: 10},
- view: {defaultValue: ""}
+ view: {defaultValue: ""},
+ stale: {defaultValue: false}
});
var viewName = (urlParts.length > 0) ? urlParts.join("/") : null;
@@ -117,7 +122,7 @@
if (viewName) {
this.redirecting = true;
location.href = "database.html?" + encodeURIComponent(dbName) +
- "/" + viewName;
+ "/" + encodeURIComponent(viewName);
}
}
var db = $.couch.db(dbName);
@@ -133,47 +138,33 @@
var templates = {
javascript: "function(doc) {\n emit(null, doc);\n}",
python: "def fun(doc):\n yield None, doc",
- ruby: "{|doc|\n emit(nil, doc);\n}"
+ ruby: "lambda {|doc|\n emit(nil, doc);\n}"
}
this.newDocument = function() {
location.href = "document.html?" + encodeURIComponent(db.name);
}
- this.compactDatabase = function() {
- $.showDialog("dialog/_compact_database.html", {
- submit: function(data, callback) {
- db.compact({
- success: function(resp) {
- callback();
- }
- });
- }
- });
- }
-
- this.viewCleanup = function() {
- $.showDialog("dialog/_view_cleanup.html", {
- submit: function(data, callback) {
- db.viewCleanup({
- success: function(resp) {
- callback();
- }
- });
- }
- });
- }
-
- this.compactView = function() {
- var groupname = page.viewName.substring(8,
- page.viewName.indexOf('/_view'));
- $.showDialog("dialog/_compact_view.html", {
+ this.compactAndCleanup = function() {
+ $.showDialog("dialog/_compact_cleanup.html", {
submit: function(data, callback) {
- db.compactView(groupname, {
- success: function(resp) {
- callback();
- }
- });
+ switch (data.action) {
+ case "compact_database":
+ db.compact({success: function(resp) { callback() }});
+ break;
+ case "compact_views":
+ var idx = page.viewName.indexOf("/_view");
+ if (idx == -1) {
+ alert("Compact Views requires focus on a view!");
+ } else {
+ var groupname = page.viewName.substring(8, idx);
+ db.compactView(groupname, {success: function(resp) { callback() }});
+ }
+ break;
+ case "view_cleanup":
+ db.viewCleanup({success: function(resp) { callback() }});
+ break;
+ }
}
});
}
@@ -197,6 +188,76 @@
});
}
+ this.databaseSecurity = function() {
+ $.showDialog("dialog/_database_security.html", {
+ load : function(d) {
+ db.getDbProperty("_security", {
+ success: function(r) {
+ ["admin", "reader"].forEach(function(key) {
+ var names = [];
+ var roles = [];
+
+ if (r && typeof r[key + "s"] === "object") {
+ if ($.isArray(r[key + "s"]["names"])) {
+ names = r[key + "s"]["names"];
+ }
+ if ($.isArray(r[key + "s"]["roles"])) {
+ roles = r[key + "s"]["roles"];
+ }
+ }
+
+ $("input[name=" + key + "_names]", d).val(JSON.stringify(names));
+ $("input[name=" + key + "_roles]", d).val(JSON.stringify(roles));
+ });
+ }
+ });
+ },
+ // maybe this should be 2 forms
+ submit: function(data, callback) {
+ var errors = {};
+ var secObj = {
+ admins: {
+ names: [],
+ roles: []
+ },
+ readers: {
+ names: [],
+ roles: []
+ }
+ };
+
+ ["admin", "reader"].forEach(function(key) {
+ var names, roles;
+
+ try {
+ names = JSON.parse(data[key + "_names"]);
+ } catch(e) { }
+ try {
+ roles = JSON.parse(data[key + "_roles"]);
+ } catch(e) { }
+
+ if ($.isArray(names)) {
+ secObj[key + "s"]["names"] = names;
+ } else {
+ errors[key + "_names"] = "The " + key +
+ " names must be an array of strings";
+ }
+ if ($.isArray(roles)) {
+ secObj[key + "s"]["roles"] = roles;
+ } else {
+ errors[key + "_roles"] = "The " + key +
+ " roles must be an array of strings";
+ }
+ });
+
+ if ($.isEmptyObject(errors)) {
+ db.setDbProperty("_security", secObj);
+ }
+ callback(errors);
+ }
+ });
+ }
+
this.populateViewEditor = function() {
if (viewName.match(/^_design\//)) {
page.revertViewChanges(function() {
@@ -318,7 +379,8 @@
var path = $.couch.encodeDocId(doc._id) + "/_view/" +
encodeURIComponent(viewNames[j]);
var option = $(document.createElement("option"))
- .attr("value", path).text(viewNames[j]).appendTo(optGroup);
+ .attr("value", path).text(encodeURIComponent(viewNames[j]))
+ .appendTo(optGroup);
if (path == viewName) {
option[0].selected = true;
}
@@ -354,7 +416,7 @@
}
var viewCode = resp.views[localViewName];
page.viewLanguage = resp.language || "javascript";
- $("#language").val(page.viewLanguage);
+ $("#language").val(encodeURIComponent(page.viewLanguage));
page.updateViewEditor(viewCode.map, viewCode.reduce || "");
$("#viewcode button.revert, #viewcode button.save").attr("disabled", "disabled");
page.storedViewCode = viewCode;
@@ -366,7 +428,7 @@
page.updateViewEditor(page.storedViewCode.map,
page.storedViewCode.reduce || "");
page.viewLanguage = page.storedViewLanguage;
- $("#language").val(page.viewLanguage);
+ $("#language").val(encodeURIComponent(page.viewLanguage));
$("#viewcode button.revert, #viewcode button.save").attr("disabled", "disabled");
page.isDirty = false;
if (callback) callback();
@@ -450,7 +512,8 @@
callback({
docid: "Cannot save to " + data.docid +
" because its language is \"" + doc.language +
- "\", not \"" + page.viewLanguage + "\"."
+ "\", not \"" +
+ encodeURIComponent(page.viewLanguage) + "\"."
});
return;
}
@@ -620,7 +683,7 @@
if (row.id) {
$("<td class='key'><a href='document.html?" + encodeURIComponent(db.name) +
"/" + $.couch.encodeDocId(row.id) + "'><strong></strong><br>" +
- "<span class='docid'>ID:&nbsp;" + row.id + "</span></a></td>")
+ "<span class='docid'>ID:&nbsp;" + $.futon.escape(row.id) + "</span></a></td>")
.find("strong").text(key).end()
.appendTo(tr);
} else {
@@ -712,6 +775,11 @@
db.query(currentMapCode, currentReduceCode, page.viewLanguage, options);
} else {
var viewParts = viewName.split('/');
+
+ if ($.futon.storage.get("stale")) {
+ options.stale = "ok";
+ }
+
db.view(viewParts[1] + "/" + viewParts[3], options);
}
}
@@ -774,7 +842,8 @@
$(this).html($("<pre></pre>").html($.futon.formatJSON(page.doc, {html: true})))
.makeEditable({allowEmpty: false,
createInput: function(value) {
- return $("<textarea rows='8' cols='80' spellcheck='false'></textarea>").enableTabInsertion();
+ var rows = value.split("\n").length;
+ return $("<textarea rows='" + rows + "' cols='80' spellcheck='false'></textarea>").enableTabInsertion();
},
prepareInput: function(input) {
$(input).makeResizable({vertical: true});
@@ -798,7 +867,7 @@
return true;
} catch (err) {
var msg = err.message;
- if (msg == "parseJSON" || msg == "JSON.parse") {
+ if (msg == "parseJSON" || msg == "JSON.parse") {
msg = "There is a syntax error in the document.";
}
$("<div class='error'></div>").text(msg).appendTo(this);
@@ -1042,10 +1111,11 @@
row.find("td").makeEditable({acceptOnBlur: false, allowEmpty: true,
createInput: function(value) {
+ value = doc[row.data("name")];
var elem = $(this);
if (elem.find("dl").length > 0 ||
elem.find("code").is(".array, .object") ||
- elem.find("code.string").text().length > 60) {
+ typeof(value) == "string" && (value.length > 60 || value.match(/\n/))) {
return $("<textarea rows='1' cols='40' spellcheck='false'></textarea>");
}
return $("<input type='text' spellcheck='false'>");
@@ -1117,7 +1187,40 @@
html: true,
escapeStrings: false
});
- return $(html);
+ var n = $(html);
+ if (n.text().length > 140) {
+ // This code reduces a long string in to a summarized string with a link to expand it.
+ // Someone, somewhere, is doing something nasty with the event after it leaves these handlers.
+ // At this time I can't track down the offender, it might actually be a jQuery propogation issue.
+ var fulltext = n.text();
+ var mintext = n.text().slice(0, 140);
+ var e = $('<a href="#expand">...</a>');
+ var m = $('<a href="#min">X</a>');
+ var expand = function (evt) {
+ n.empty();
+ n.text(fulltext);
+ n.append(m);
+ evt.stopPropagation();
+ evt.stopImmediatePropagation();
+ evt.preventDefault();
+ }
+ var minimize = function (evt) {
+ n.empty();
+ n.text(mintext);
+ // For some reason the old element's handler won't fire after removed and added again.
+ e = $('<a href="#expand">...</a>');
+ e.click(expand);
+ n.append(e);
+ evt.stopPropagation();
+ evt.stopImmediatePropagation();
+ evt.preventDefault();
+ }
+ e.click(expand);
+ n.click(minimize);
+ n.text(mintext);
+ n.append(e)
+ }
+ return n;
}
}
var elem = render(value);
@@ -1179,7 +1282,7 @@
for (var i=0; i < parts.length; i++) {
encoded.push(encodeURIComponent(parts[i]));
};
- return encoded.join('/');
+ return encoded.join('%2f');
}
})(jQuery);