summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarren Smith <garren.smith@gmail.com>2013-05-16 08:34:54 +0200
committerGarren Smith <garren.smith@gmail.com>2013-06-04 15:46:38 +0200
commit90e4da6ce6bda6cb72b325d2702fe9832e20ff59 (patch)
treed0fe10f41faba1d1595eeb688d15df9e4287ba2a
parenteb364ff3a2a85bdc15aaa4f5b8c69aa987aea6f8 (diff)
downloadcouchdb-90e4da6ce6bda6cb72b325d2702fe9832e20ff59.tar.gz
Create/edit and query views.
It is now also possible to preview views with pouch. Issue #1806
-rw-r--r--src/fauxton/app/api.js8
-rw-r--r--src/fauxton/app/modules/databases/resources.js2
-rw-r--r--src/fauxton/app/modules/documents/resources.js114
-rw-r--r--src/fauxton/app/modules/documents/routes.js186
-rw-r--r--src/fauxton/app/modules/documents/views.js591
-rw-r--r--src/fauxton/app/modules/pouchdb/base.js17
-rw-r--r--src/fauxton/app/modules/pouchdb/pouchdb.mapreduce.js50
-rw-r--r--src/fauxton/app/templates/documents/all_docs_list.html93
-rw-r--r--src/fauxton/app/templates/documents/changes.html4
-rw-r--r--src/fauxton/app/templates/documents/view_editor.html238
-rw-r--r--src/fauxton/app/templates/layouts/with_tabs_sidebar.html5
11 files changed, 790 insertions, 518 deletions
diff --git a/src/fauxton/app/api.js b/src/fauxton/app/api.js
index c506b1aa0..e13a41dcc 100644
--- a/src/fauxton/app/api.js
+++ b/src/fauxton/app/api.js
@@ -32,7 +32,8 @@ function(app, Fauxton) {
// List of JSHINT errors to ignore
// Gets around problem of anonymous functions not being a valid statement
FauxtonAPI.excludedViewErrors = [
- "Missing name in function declaration."
+ "Missing name in function declaration.",
+ "['{a}'] is better written in dot notation."
];
FauxtonAPI.isIgnorableError = function(msg) {
@@ -54,8 +55,9 @@ function(app, Fauxton) {
}
});
- FauxtonAPI.navigate = function(url) {
- Backbone.history.navigate(url, true);
+ FauxtonAPI.navigate = function(url, _opts) {
+ var options = _.extend({trigger: true}, _opts );
+ app.router.navigate(url,options);
};
FauxtonAPI.addHeaderLink = function(link) {
diff --git a/src/fauxton/app/modules/databases/resources.js b/src/fauxton/app/modules/databases/resources.js
index 6927fd57e..04e6c1ecc 100644
--- a/src/fauxton/app/modules/databases/resources.js
+++ b/src/fauxton/app/modules/databases/resources.js
@@ -47,7 +47,7 @@ function(app, FauxtonAPI, Documents) {
if (context === "index") {
return "/database/" + this.id + "/_all_docs";
} else if (context === "changes") {
- return "/database/" + this.id + "/_changes?descending=true&limit=100";
+ return "/database/" + this.id + "/_changes?descending=true&limit=100&include_docs=true";
} else if (context === "app") {
return "/database/" + this.id;
} else {
diff --git a/src/fauxton/app/modules/documents/resources.js b/src/fauxton/app/modules/documents/resources.js
index f1a07c32e..e31337887 100644
--- a/src/fauxton/app/modules/documents/resources.js
+++ b/src/fauxton/app/modules/documents/resources.js
@@ -13,15 +13,10 @@
define([
"app",
- "api",
-
- // Views
- "modules/documents/views"
-
- // Plugins
+ "api"
],
-function(app, FauxtonAPI, Views) {
+function(app, FauxtonAPI) {
var Documents = app.module();
Documents.Doc = Backbone.Model.extend({
@@ -64,14 +59,61 @@ function(app, FauxtonAPI, Views) {
hasViews: function() {
if (!this.isDdoc()) return false;
var doc = this.get('doc');
- return doc && doc.views && _.keys(doc.views).length > 0;
+ if (doc) {
+ return doc && doc.views && _.keys(doc.views).length > 0;
+ }
+
+ var views = this.get('views');
+ return views && _.keys(views).length > 0;
},
getDdocView: function(view) {
if (!this.isDdoc() || !this.hasViews()) return false;
var doc = this.get('doc');
- return doc.views[view];
+ if (doc) {
+ return doc.views[view];
+ }
+
+ return this.get('views')[view];
+ },
+
+ setDdocView: function (view, map, reduce) {
+ if (!this.isDdoc()) return false;
+ var views = this.get('views');
+
+ if (reduce) {
+ views[view] = {
+ map: map,
+ reduce: reduce
+ };
+ } else {
+ views[view].map = map;
+ }
+
+ this.set({views: views});
+
+ return true;
+ },
+
+ removeDdocView: function (viewName) {
+ if (!this.isDdoc()) return false;
+ var views = this.get('views');
+
+ delete views[viewName];
+ this.set({views: views});
+ },
+
+ dDocModel: function () {
+ if (!this.isDdoc()) return false;
+ var doc = this.get('doc');
+
+ if (doc) {
+ console.log('DOC', doc);
+ return new Documents.Doc(doc, {database: this.database});
+ }
+
+ return this;
},
viewHasReduce: function(viewName) {
@@ -208,10 +250,10 @@ function(app, FauxtonAPI, Views) {
initialize: function(_models, options) {
this.database = options.database;
- this.view = options.view;
- this.design = options.design;
this.params = _.extend({limit: 10, reduce: false}, options.params);
this.idxType = "_view";
+ this.view = options.view;
+ this.design = options.design.replace('_design/','');
},
url: function() {
@@ -256,8 +298,56 @@ function(app, FauxtonAPI, Views) {
return this.models;
}
});
+
+ Documents.PouchIndexCollection = Backbone.Collection.extend({
+ model: Documents.ViewRow,
+
+ initialize: function(_models, options) {
+ this.database = options.database;
+ this.rows = options.rows;
+ this.view = options.view;
+ this.design = options.design.replace('_design/','');
+ this.params = _.extend({limit: 10, reduce: false}, options.params);
+ this.idxType = "_view";
+ },
+
+ url: function () {
+ return '';
+ },
+
+ fetch: function() {
+ var deferred = FauxtonAPI.Deferred();
+ this.reset(this.rows, {silent: true});
+
+ this.viewMeta = {
+ total_rows: this.rows.length,
+ offest: 0,
+ update_seq: false
+ };
+
+ deferred.resolve();
+ return deferred;
+ },
+
+ totalRows: function() {
+ console.log('rows');
+ console.log(this);
+ return this.viewMeta.total_rows || "unknown";
+ },
+
+ updateSeq: function() {
+ return this.viewMeta.update_seq || false;
+ },
+
+ buildAllDocs: function(){
+ this.fetch();
+ },
+
+ allDocs: function(){
+ return this.models;
+ }
+ });
- Documents.Views = Views;
return Documents;
});
diff --git a/src/fauxton/app/modules/documents/routes.js b/src/fauxton/app/modules/documents/routes.js
index 1a755591f..e02ac936f 100644
--- a/src/fauxton/app/modules/documents/routes.js
+++ b/src/fauxton/app/modules/documents/routes.js
@@ -16,7 +16,7 @@ define([
"api",
// Modules
- "modules/documents/resources",
+ "modules/documents/views",
"modules/databases/base"
],
@@ -32,7 +32,7 @@ function(app, FauxtonAPI, Documents, Databases) {
var databaseName = options[0], docID = options[1];
this.database = this.database || new Databases.Model({id: databaseName});
- this.doc = this.doc || new Documents.Doc({
+ this.doc = new Documents.Doc({
_id: docID
}, {
database: this.database
@@ -62,7 +62,8 @@ function(app, FauxtonAPI, Documents, Databases) {
code_editor: function (event) {
this.tabsView.updateSelected('code_editor');
this.docView = this.setView("#dashboard-content", new Documents.Views.Doc({
- model: this.doc
+ model: this.doc,
+ database: this.database
}));
},
@@ -78,96 +79,6 @@ function(app, FauxtonAPI, Documents, Databases) {
}
});
- /*var newViewEditorCallback = function(databaseName) {
- var data = {
- database: new Databases.Model({id:databaseName})
- };
- data.designDocs = new Documents.AllDocs(null, {
- database: data.database,
- params: {startkey: '"_design"',
- endkey: '"_design1"',
- include_docs: true}
- });
-
- return {
- layout: "with_tabs_sidebar",
-
- data: data,
-
- crumbs: [
- {"name": "Databases", "link": "/_all_dbs"},
- {"name": data.database.id, "link": data.database.url('app')}
- ],
-
- views: {
- "#sidebar-content": new Documents.Views.Sidebar({
- collection: data.designDocs
- }),
-
- "#tabs": new Documents.Views.Tabs({
- collection: data.designDocs,
- database: data.database
- }),
-
- "#dashboard-content": new Documents.Views.ViewEditor({
- model: data.database,
- ddocs: data.designDocs
- })
- },
-
- apiUrl: data.database.url()
- };
- };*/
-
- // HACK: this kind of works
- // Basically need a way to share state between different routes, for
- // instance making a new doc won't work for switching back and forth
- // between code and field editors
- /*var newDocCodeEditorCallback = function(databaseName) {
- var data = {
- database: new Databases.Model({id:databaseName}),
- doc: new Documents.NewDoc(),
- selected: "code_editor"
- };
- data.doc.database = data.database;
- data.designDocs = new Documents.AllDocs(null, {
- database: data.database,
- params: {startkey: '"_design"',
- endkey: '"_design1"',
- include_docs: true}
- });
-
- var options = app.getParams();
- options.include_docs = true;
- data.database.buildAllDocs(options);
-
- return {
- layout: "one_pane",
-
- data: data,
-
- crumbs: [
- {"name": "Databases", "link": "/_all_dbs"},
- {"name": data.database.id, "link": Databases.databaseUrl(data.database)},
- {"name": "new", "link": "#"}
- ],
-
- views: {
- "#dashboard-content": new Documents.Views.Doc({
- model: data.doc
- }),
-
- "#tabs": new Documents.Views.FieldEditorTabs({
- selected: data.selected,
- model: data.doc
- })
- },
-
- apiUrl: data.doc.url()
- };
- };*/
-
-
var DocumentsRouteObject = FauxtonAPI.RouteObject.extend({
layout: "with_tabs_sidebar",
@@ -180,6 +91,12 @@ function(app, FauxtonAPI, Documents, Databases) {
"database/:database/new_view": "newViewEditor"
},
+ events: {
+ "route:updateAllDocs": "updateAllDocsFromView",
+ "route:updatePreviewDocs": "updateAllDocsFromPreview",
+ "route:reloadDesignDocs": "reloadDesignDocs"
+ },
+
initialize: function (route, masterLayout, options) {
var docOptions = app.getParams();
docOptions.include_docs = true;
@@ -207,6 +124,9 @@ function(app, FauxtonAPI, Documents, Databases) {
}));
},
+ establish: function () {
+ return this.data.designDocs.fetch();
+ },
allDocs: function(databaseName, options) {
var docOptions = app.getParams(options);
@@ -220,7 +140,9 @@ function(app, FauxtonAPI, Documents, Databases) {
this.sidebar.setSelectedTab('all-docs');
}
- this.documentsView = this.setView("#dashboard-content", new Documents.Views.AllDocsList({
+ if (this.viewEditor) { this.viewEditor.remove(); }
+
+ this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({
collection: this.data.database.allDocs
}));
@@ -250,12 +172,22 @@ function(app, FauxtonAPI, Documents, Databases) {
designDocs: this.data.designDocs
};
- this.setView("#dashboard-content", new Documents.Views.AllDocsList({
+ this.viewEditor = this.setView("#dashboard-upper-content", new Documents.Views.ViewEditor({
+ model: this.data.database,
+ ddocs: this.data.designDocs,
+ viewName: view,
+ params: params,
+ newView: false,
+ database: this.data.database,
+ ddocInfo: ddocInfo
+ }));
+
+ this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({
+ database: this.data.database,
collection: this.data.indexedDocs,
nestedView: Documents.Views.Row,
viewList: true,
- ddocInfo: ddocInfo,
- params: params
+ ddocInfo: ddocInfo
}));
this.sidebar.setSelectedTab(ddoc + '_' + view);
@@ -271,17 +203,65 @@ function(app, FauxtonAPI, Documents, Databases) {
this.apiUrl = this.data.indexedDocs.url();
},
- newViewEditor: function (event) {
- // TODO: Get this working
- this.setView("#dashboard-content", new Documents.Views.ViewEditor({
- model: this.data.database,
- ddocs: this.data.designDocs
+ newViewEditor: function () {
+ var params = app.getParams();
+
+ this.viewEditor = this.setView("#dashboard-upper-content", new Documents.Views.ViewEditor({
+ ddocs: this.data.designDocs,
+ params: params,
+ database: this.data.database,
+ newView: true
}));
this.sidebar.setSelectedTab('new-view');
+ },
- }
+ updateAllDocsFromView: function (event) {
+ var view = event.view,
+ ddoc = event.ddoc;
+
+ this.data.indexedDocs = new Documents.IndexCollection(null, {
+ database: this.data.database,
+ design: ddoc,
+ view: view,
+ params: app.getParams()
+ });
+
+ this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({
+ database: this.data.database,
+ collection: this.data.indexedDocs,
+ nestedView: Documents.Views.Row,
+ viewList: true
+ }));
+ },
+
+ updateAllDocsFromPreview: function (event) {
+ var view = event.view,
+ rows = event.rows,
+ ddoc = event.ddoc;
+ this.data.indexedDocs = new Documents.PouchIndexCollection(null, {
+ database: this.data.database,
+ design: ddoc,
+ view: view,
+ rows: rows
+ });
+
+ this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({
+ database: this.data.database,
+ collection: this.data.indexedDocs,
+ nestedView: Documents.Views.Row,
+ viewList: true
+ }));
+ },
+
+ reloadDesignDocs: function (event) {
+ this.sidebar.forceRender();
+
+ if (event && event.selectedTab) {
+ this.sidebar.setSelectedTab(event.selectedTab);
+ }
+ }
});
var ChangesRouteObject = FauxtonAPI.RouteObject.extend({
diff --git a/src/fauxton/app/modules/documents/views.js b/src/fauxton/app/modules/documents/views.js
index 17bf9af9d..f5cd88298 100644
--- a/src/fauxton/app/modules/documents/views.js
+++ b/src/fauxton/app/modules/documents/views.js
@@ -11,20 +11,24 @@
// the License.
define([
- "app",
+ "app",
- "api",
+ "api",
- // Libs
- "codemirror",
- "jshint",
+ "modules/documents/resources",
+ "modules/pouchdb/base",
+
+ // Libs
+ "codemirror",
+ "jshint",
+
+ // Plugins
+ "plugins/codemirror-javascript",
+ "plugins/prettify"
- // Plugins
- "plugins/codemirror-javascript",
- "plugins/prettify"
],
-function(app, FauxtonAPI, Codemirror, JSHint) {
+function(app, FauxtonAPI, Documents, pouchdb, Codemirror, JSHint) {
var Views = {};
Views.Tabs = FauxtonAPI.View.extend({
@@ -276,161 +280,50 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
template: "templates/documents/all_docs_list",
events: {
"click button.all": "selectAll",
- "click button.bulk-delete": "bulkDelete",
- "change form.view-query-update input": "updateFilters",
- "change form.view-query-update select": "updateFilters",
- "submit form.view-query-update": "updateView"
+ "click button.bulk-delete": "bulkDelete"
},
initialize: function(options){
this.nestedView = options.nestedView || Views.Document;
this.rows = {};
this.viewList = !! options.viewList;
- this.params = options.params;
+ this.database = options.database;
if (options.ddocInfo) {
this.designDocs = options.ddocInfo.designDocs;
this.ddocID = options.ddocInfo.id;
}
+ this.newView = options.newView || false;
},
establish: function() {
- var deferreds = [
- this.collection.fetch().error(function() {
- // TODO: handle error requests that slip through
- // This should just throw a notification, not break the page
- console.log("ERROR: ", arguments);
- })
- ];
- if (this.designDocs) {
- deferreds.push(this.designDocs.fetch());
- }
- return deferreds;
+ if (this.newView) { return null; }
+
+ return this.collection.fetch().fail(function() {
+ // TODO: handle error requests that slip through
+ // This should just throw a notification, not break the page
+ console.log("ERROR: ", arguments);
+ });
},
selectAll: function(evt){
$("input:checkbox").attr('checked', !$(evt.target).hasClass('active'));
},
- // TODO:: HACK::
- // Hack to grab info about the ddoc and current view to determine whether
- // or not the view has a reduce function so we can display the advanced
- // options appropriately.
- //
- // NOTE: we have this here temporarily because we have to wait for the
- // design docs to be present.
- //
- // NOTE: We should probably refactor this View out into a separate View
- // dedicated to displaying view query results.
- // If nothing else, we should at least switch to something along the lines
- // of fetchOnce to ensure we're not reloading the ddocs here in addition to
- // the sidebar.
- setDdocInfo: function() {
- if (!this.ddoc && this.designDocs) {
- this.ddoc = this.designDocs.get(this.ddocID);
- }
- },
-
serialize: function() {
- this.setDdocInfo();
- var data = {
- database: this.collection,
- viewList: this.viewList,
- hasReduce: false,
- params: this.params,
- ddocs: this.designDocs
- };
- if (this.ddoc) {
- data.ddoc = this.ddoc;
- data.hasReduce = this.ddoc.viewHasReduce(this.collection.view);
- }
- return data;
- },
-
- updateView: function(event) {
- event.preventDefault();
- var $form = $(event.currentTarget);
+ var totalRows = 0,
+ updateSeq = false;
- // Ignore params without a value
- var params = _.filter($form.serializeArray(), function(param) {
- return param.value;
- });
-
- // Validate *key* params to ensure they're valid JSON
- var keyParams = ["key","keys","startkey","endkey"];
- var errorParams = _.filter(params, function(param) {
- if (_.contains(keyParams, param.name)) {
- try {
- JSON.parse(param.value);
- return false;
- } catch(e) {
- return true;
- }
- } else {
- return false;
- }
- });
-
- if (_.any(errorParams)) {
- _.map(errorParams, function(param) {
-
- // TODO: Where to add this error?
- // bootstrap wants the error on a control-group div, but we're not using that
- //$('form.view-query-update input[name='+param+'], form.view-query-update select[name='+param+']').addClass('error');
-
- return FauxtonAPI.addNotification({
- msg: "JSON Parse Error on field: "+param.name,
- type: "error",
- selector: ".view.show .all-docs-list.errors-container"
- });
- });
-
- FauxtonAPI.addNotification({
- msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
- type: "warning",
- selector: ".view.show .all-docs-list.errors-container"
- });
-
- return false;
+ if (!this.newView) {
+ totalRows = this.collection.totalRows();
+ updateSeq = this.collection.updateSeq();
}
- var fragment = window.location.hash.replace(/\?.*$/, '');
- fragment = fragment + '?' + $.param(params);
- FauxtonAPI.navigate(fragment);
- },
-
- updateFilters: function(event) {
- event.preventDefault();
- var $ele = $(event.currentTarget);
- var name = $ele.attr('name');
- this.updateFiltersFor(name, $ele);
- },
-
- updateFiltersFor: function(name, $ele) {
- var $form = $ele.parents("form.view-query-update:first");
- switch (name) {
- // Reduce constraints
- // - Can't include_docs for reduce=true
- // - can't include group_level for reduce=false
- case "reduce":
- if ($ele.prop('checked') === true) {
- if ($form.find("input[name=include_docs]").prop("checked") === true) {
- $form.find("input[name=include_docs]").prop("checked", false);
- var notification = FauxtonAPI.addNotification({
- msg: "include_docs has been disabled as you cannot include docs on a reduced view",
- type: "warn",
- selector: ".view.show .all-docs-list.errors-container"
- });
- }
- $form.find("input[name=include_docs]").prop("disabled", true);
- $form.find("select[name=group_level]").prop("disabled", false);
- } else {
- $form.find("select[name=group_level]").prop("disabled", true);
- $form.find("input[name=include_docs]").prop("disabled", false);
- }
- break;
- case "include_docs":
- break;
- }
+ return {
+ updateSeq: updateSeq,
+ totalRows: totalRows,
+ numModels: this.collection.models.length,
+ viewList: this.viewList
+ };
},
/*
@@ -468,14 +361,6 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
},
beforeRender: function() {
- this.setDdocInfo();
- if (this.viewList) {
- this.viewEditorView = this.insertView("#edit-index-container", new Views.ViewEditor({
- model: this.ddoc,
- ddocs: this.designDocs,
- viewCollection: this.collection
- }));
- }
this.collection.each(function(doc) {
this.rows[doc.id] = this.insertView("table.all-docs tbody", new this.nestedView({
model: doc
@@ -485,34 +370,6 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
afterRender: function(){
prettyPrint();
- if (this.params) {
- var $form = this.$el.find("form.view-query-update");
- _.each(this.params, function(val, key) {
- var $ele;
- switch (key) {
- case "limit":
- case "group_level":
- $form.find("select[name='"+key+"']").val(val);
- break;
- case "include_docs":
- case "stale":
- case "descending":
- case "inclusive_end":
- $form.find("input[name='"+key+"']").prop('checked', true);
- break;
- case "reduce":
- $ele = $form.find("input[name='"+key+"']");
- if (val == "true") {
- $ele.prop('checked', true);
- }
- this.updateFiltersFor(key, $ele);
- break;
- default:
- $form.find("input[name='"+key+"']").val(val);
- break;
- }
- }, this);
- }
}
});
@@ -523,6 +380,10 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
"click button.save-doc": "saveDoc"
},
+ initialize: function (options) {
+ this.database = options.database;
+ },
+
updateValues: function() {
var notification;
if (this.model.changedAttributes()) {
@@ -540,13 +401,15 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
},
saveDoc: function(event) {
- var json, notification;
+ var json, notification, that = this;
if (this.hasValidCode()) {
json = JSON.parse(this.editor.getValue());
this.model.clear({silent:true});
this.model.set(json);
notification = FauxtonAPI.addNotification({msg: "Saving document."});
- this.model.save().error(function(xhr) {
+ this.model.save().then(function () {
+ FauxtonAPI.navigate('/database/' + that.database.id + '/' + that.model.id);
+ }).fail(function(xhr) {
var responseText = JSON.parse(xhr.responseText).reason;
notification = FauxtonAPI.addNotification({
msg: "Save failed: " + responseText,
@@ -607,7 +470,8 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
matchBrackets: true,
lineWrapping: true,
onChange: function() {
- that.runJSHint();
+ //throwing errors at the moment
+ //that.runJSHint();
},
extraKeys: {
"Ctrl-S": function(instance) { that.saveDoc(); },
@@ -646,6 +510,7 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
}
});
+ //TODO split this into two smaller views, one for advance query options and other for index editing
Views.ViewEditor = FauxtonAPI.View.extend({
template: "templates/documents/view_editor",
builtinReduces: ['_sum', '_count', '_stats'],
@@ -653,12 +518,17 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
events: {
"click button.save": "saveView",
"click button.preview": "previewView",
- "change select#reduce-function-selector": "updateReduce"
+ "click button.delete": "deleteView",
+ "change select#reduce-function-selector": "updateReduce",
+ "change form.view-query-update input": "updateFilters",
+ "change form.view-query-update select": "updateFilters",
+ "change select#ddoc": "updateDesignDoc",
+ "submit form.view-query-update": "updateView"
},
langTemplates: {
"javascript": {
- map: "function(doc) {\n emit(null, doc);\n}",
+ map: "function(doc) {\n emit(doc.id, 1);\n}",
reduce: "function(keys, values, rereduce){\n if (rereduce){\n return sum(values);\n } else {\n return values.length;\n }\n}"
}
},
@@ -666,10 +536,27 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
defaultLang: "javascript",
initialize: function(options) {
+ this.newView = options.newView || false;
this.ddocs = options.ddocs;
- this.viewCollection = options.viewCollection;
- this.reduceFunStr = this.model.viewHasReduce(this.viewCollection.view);
- this.newView = false;
+ this.params = options.params;
+ this.database = options.database;
+ if (this.newView) {
+ this.viewName = 'newView';
+ } else {
+ this.ddocID = options.ddocInfo.id;
+ this.viewName = options.viewName;
+ }
+ },
+
+ updateDesignDoc: function () {
+
+ if (this.$('#ddoc :selected').prop('id') === 'new-doc') {
+ this.$('#new-ddoc-section').show();
+
+ } else {
+ this.$('#new-ddoc-section').hide();
+ }
+
},
updateValues: function() {
@@ -694,43 +581,211 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
}
},
- establish: function() {
- //return [this.ddocs.fetch(), this.model.fetch()];
- return [];
+ queryParams: function () {
+ var $form = $(".view-query-update");
+ // Ignore params without a value
+ var params = _.filter($form.serializeArray(), function(param) {
+ return param.value;
+ });
+
+ // Validate *key* params to ensure they're valid JSON
+ var keyParams = ["key","keys","startkey","endkey"];
+ var errorParams = _.filter(params, function(param) {
+ if (_.contains(keyParams, param.name)) {
+ try {
+ JSON.parse(param.value);
+ return false;
+ } catch(e) {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ });
+
+ return {params: params, errorParams: errorParams};
+ },
+
+ deleteView: function (event) {
+ event.preventDefault();
+
+ if (this.newView) { return alert('Cannot delete a new view.'); }
+ if (!confirm('Are you sure you want to delete this view?')) {return;}
+
+ var that = this,
+ promise,
+ viewName = this.$('#index-name').val();
+ ddocName = this.$('#ddoc :selected').val(),
+ ddoc = this.getCurrentDesignDoc();
+
+ ddoc.removeDdocView(viewName);
+
+ if (ddoc.hasViews()) {
+ promise = ddoc.save();
+ } else {
+ promise = ddoc.destroy();
+ }
+
+ promise.then(function () {
+ FauxtonAPI.navigate('/database/' + that.database.id + '/_all_docs?limit=100');
+ FauxtonAPI.triggerRouteEvent('reloadDesignDocs');
+ });
+ },
+
+ updateView: function(event) {
+ event.preventDefault();
+
+ if (this.newView) { return alert('Please save this new view before querying it.'); }
+
+ var paramInfo = this.queryParams(),
+ errorParams = paramInfo.errorParams,
+ params = paramInfo.params;
+
+ if (_.any(errorParams)) {
+ _.map(errorParams, function(param) {
+
+ // TODO: Where to add this error?
+ // bootstrap wants the error on a control-group div, but we're not using that
+ //$('form.view-query-update input[name='+param+'], form.view-query-update select[name='+param+']').addClass('error');
+
+ return FauxtonAPI.addNotification({
+ msg: "JSON Parse Error on field: "+param.name,
+ type: "error",
+ selector: ".view.show .all-docs-list.errors-container"
+ });
+ });
+
+ FauxtonAPI.addNotification({
+ msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
+ type: "warning",
+ selector: ".view.show .all-docs-list.errors-container"
+ });
+
+ return false;
+ }
+
+ var fragment = window.location.hash.replace(/\?.*$/, '');
+ fragment = fragment + '?' + $.param(params);
+ FauxtonAPI.navigate(fragment, {trigger: false});
+
+ FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: this.ddocID, view: this.viewName});
+ },
+
+ updateFilters: function(event) {
+ event.preventDefault();
+ var $ele = $(event.currentTarget);
+ var name = $ele.attr('name');
+ this.updateFiltersFor(name, $ele);
+ },
+
+ updateFiltersFor: function(name, $ele) {
+ var $form = $ele.parents("form.view-query-update:first");
+ switch (name) {
+ // Reduce constraints
+ // - Can't include_docs for reduce=true
+ // - can't include group_level for reduce=false
+ case "reduce":
+ if ($ele.prop('checked') === true) {
+ if ($form.find("input[name=include_docs]").prop("checked") === true) {
+ $form.find("input[name=include_docs]").prop("checked", false);
+ var notification = FauxtonAPI.addNotification({
+ msg: "include_docs has been disabled as you cannot include docs on a reduced view",
+ type: "warn",
+ selector: ".view.show .all-docs-list.errors-container"
+ });
+ }
+ $form.find("input[name=include_docs]").prop("disabled", true);
+ $form.find("select[name=group_level]").prop("disabled", false);
+ } else {
+ $form.find("select[name=group_level]").prop("disabled", true);
+ $form.find("input[name=include_docs]").prop("disabled", false);
+ }
+ break;
+ case "include_docs":
+ break;
+ }
},
previewView: function(event) {
+ var that = this,
+ mapVal = this.mapEditor.getValue(),
+ reduceVal = this.reduceVal(),
+ paramsArr = this.queryParams().params;
+
+ var params = _.reduce(paramsArr, function (params, param) {
+ params[param.name] = param.value;
+ return params;
+ }, {reduce: false});
+
+ event.preventDefault();
+
FauxtonAPI.addNotification({
msg: "<strong>Warning!</strong> Preview executes the Map/Reduce functions in your browser, and may behave differently from CouchDB.",
type: "warning",
selector: "#define-view .errors-container",
- fade: false
+ fade: true
});
- FauxtonAPI.addNotification({
- msg: "Preview Functionality Coming Soon",
- type: "warning",
- selector: "#define-view .errors-container"
+
+ var promise = FauxtonAPI.Deferred();
+
+ if (!this.database.allDocs) {
+ this.database.buildAllDocs({limit: "100", include_docs: true});
+ promise = this.database.allDocs.fetch();
+ } else {
+ promise.resolve();
+ }
+
+ promise.then(function () {
+ params.docs = that.database.allDocs.map(function (model) { return model.get('doc');});
+
+ var queryPromise = pouchdb.runViewQuery({map: mapVal, reduce: reduceVal}, params);
+ queryPromise.then(function (results) {
+ FauxtonAPI.triggerRouteEvent('updatePreviewDocs', {rows: results.rows, ddoc: that.getCurrentDesignDoc().id, view: that.viewName});
+ });
});
},
saveView: function(event) {
- var json, notification;
+ var json, notification,
+ that = this;
+
+ event.preventDefault();
+
if (this.hasValidCode()) {
- var mapVal = this.mapEditor.getValue();
- var reduceVal = this.reduceEditor.getValue();
- /*
+ var mapVal = this.mapEditor.getValue(),
+ reduceVal = this.reduceVal(),
+ viewName = this.$('#index-name').val(),
+ ddoc = this.getCurrentDesignDoc(),
+ ddocName = ddoc.id;
+
+ this.viewName = viewName;
+
notification = FauxtonAPI.addNotification({
msg: "Saving document.",
selector: "#define-view .errors-container"
});
- */
- FauxtonAPI.addNotification({
- msg: "Save Functionality Coming Soon",
- type: "warning",
- selector: "#define-view .errors-container"
- });
- /*
- this.model.save().error(function(xhr) {
+
+ ddoc.setDdocView(viewName, mapVal, reduceVal);
+
+ ddoc.save().then(function () {
+ FauxtonAPI.addNotification({
+ msg: "View has been saved.",
+ type: "success",
+ selector: "#define-view .errors-container"
+ });
+
+ if (that.newView) {
+ var fragment = '/database/' + that.database.id +'/' + ddocName + '/_view/' + viewName;
+
+ FauxtonAPI.navigate(fragment, {trigger: false});
+ FauxtonAPI.triggerRouteEvent('reloadDesignDocs',{selectedTab: ddocName.replace('_design/','') + '_' + viewName});
+
+ that.newView = false;
+ }
+
+ FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: ddocName, view: viewName});
+
+ }, function(xhr) {
var responseText = JSON.parse(xhr.responseText).reason;
notification = FauxtonAPI.addNotification({
msg: "Save failed: " + responseText,
@@ -738,21 +793,51 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
clear: true
});
});
- */
} else {
notification = FauxtonAPI.addNotification({
- msg: "Please fix the JSON errors and try again.",
+ msg: "Please fix the Javascript errors and try again.",
type: "error",
selector: "#define-view .errors-container"
});
}
},
+ getCurrentDesignDoc: function () {
+ if (this.newDesignDoc()) {
+ var doc = {
+ _id: '_design/' + this.$('#new-ddoc').val(),
+ views: {},
+ language: "javascript"
+ };
+ return new Documents.Doc(doc, {database: this.database});
+ } else {
+ var ddocName = this.$('#ddoc').val();
+ return this.ddocs.find(function (ddoc) {
+ return ddoc.id === ddocName;
+ }).dDocModel();
+ }
+
+ },
+
+ newDesignDoc: function () {
+ return this.$('#ddoc :selected').prop('id') === 'new-doc';
+ },
+
isCustomReduceEnabled: function() {
return $("#reduce-function-selector").val() == "CUSTOM";
},
reduceVal: function() {
+ var reduceOption = this.$('#reduce-function-selector :selected').val(),
+ reduceVal = "";
+
+ if (reduceOption === 'CUSTOM') {
+ reduceVal = this.reduceEditor.getValue();
+ } else if ( reduceOption !== 'NONE') {
+ reduceVal = reduceOption;
+ }
+
+ return reduceVal;
},
hasValidCode: function() {
@@ -765,7 +850,7 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
} else {
// By default CouchDB view functions don't pass lint
return _.every(JSHINT.errors, function(error) {
- return FauxtonAPI.isIgnorableError(error.reason);
+ return FauxtonAPI.isIgnorableError(error.raw);
});
}
}, this);
@@ -801,13 +886,15 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
serialize: function() {
return {
- //database: this.model,
ddocs: this.ddocs,
ddoc: this.model,
- viewCollection: this.viewCollection,
+ ddocName: this.model.id,
+ viewName: this.viewName,
reduceFunStr: this.reduceFunStr,
+ hasReduce: this.reduceFunStr,
isCustomReduce: this.hasCustomReduce(),
- newView: this.newView
+ newView: this.newView,
+ langTemplates: this.langTemplates.javascript
};
},
@@ -815,6 +902,22 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
return this.reduceFunStr && ! _.contains(this.builtinReduces, this.reduceFunStr);
},
+ beforeRender: function () {
+
+ if (this.newView) {
+ this.reduceFunStr = '_sum';
+ if (this.ddocs.length === 0) {
+ this.model = new Documents.Doc(null, {database: this.database});
+ } else {
+ this.model = this.ddocs.first().dDocModel();
+ }
+ this.ddocID = this.model.id;
+ } else {
+ this.model = this.ddocs.get(this.ddocID).dDocModel();
+ this.reduceFunStr = this.model.viewHasReduce(this.viewName);
+ }
+ },
+
afterRender: function() {
var that = this;
var mapFun = $("#map-function");
@@ -823,13 +926,16 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
mapFun.val(this.langTemplates[this.defaultLang].map);
reduceFun.val(this.langTemplates[this.defaultLang].reduce);
}
+
+ this.updateDesignDoc();
+
this.mapEditor = Codemirror.fromTextArea(mapFun.get()[0], {
mode: "javascript",
lineNumbers: true,
matchBrackets: true,
lineWrapping: true,
onChange: function() {
- that.runJSHint("mapEditor");
+ //that.runJSHint("mapEditor");
},
extraKeys: {
"Ctrl-S": function(instance) { that.saveView(); },
@@ -842,7 +948,7 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
matchBrackets: true,
lineWrapping: true,
onChange: function() {
- that.runJSHint("reduceEditor");
+ //that.runJSHint("reduceEditor");
},
extraKeys: {
"Ctrl-S": function(instance) { that.saveView(); },
@@ -855,6 +961,36 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
if ( ! this.hasCustomReduce()) {
$(".control-group.reduce-function").hide();
}
+
+ if (this.params) {
+ var $form = this.$el.find("form.view-query-update");
+ _.each(this.params, function(val, key) {
+ var $ele;
+ switch (key) {
+ case "limit":
+ case "group_level":
+ $form.find("select[name='"+key+"']").val(val);
+ break;
+ case "include_docs":
+ case "stale":
+ case "descending":
+ case "inclusive_end":
+ $form.find("input[name='"+key+"']").prop('checked', true);
+ break;
+ case "reduce":
+ $ele = $form.find("input[name='"+key+"']");
+ if (val == "true") {
+ $ele.prop('checked', true);
+ }
+ this.updateFiltersFor(key, $ele);
+ break;
+ default:
+ $form.find("input[name='"+key+"']").val(val);
+ break;
+ }
+ }, this);
+ }
+
}
});
@@ -871,14 +1007,6 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
}
},
- establish: function() {
- if (this.collection) {
- return [this.collection.fetch()];
- } else {
- return null;
- }
- },
-
serialize: function() {
return {
index: [1,2,3],
@@ -929,7 +1057,14 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
}, this);
},
+ afterRender: function () {
+ if (this.selectedTab) {
+ this.setSelectedTab(this.selectedTab);
+ }
+ },
+
setSelectedTab: function (selectedTab) {
+ this.selectedTab = selectedTab;
this.$('li').removeClass('active');
this.$('#' + selectedTab).parent().addClass('active');
}
@@ -942,20 +1077,24 @@ function(app, FauxtonAPI, Codemirror, JSHint) {
template: "templates/documents/changes",
establish: function() {
- return [
- this.model.changes.fetch()
- ];
+ return [ this.model.changes.fetch()];
},
serialize: function () {
+ console.log('c', this.model.changes.toJSON());
return {
changes: this.model.changes.toJSON(),
database: this.model
};
+ },
+
+ afterRender: function(){
+ prettyPrint();
}
- });
+ });
- return Views;
+ Documents.Views = Views;
+ return Documents;
});
diff --git a/src/fauxton/app/modules/pouchdb/base.js b/src/fauxton/app/modules/pouchdb/base.js
index 2b7cfc927..ddaf06d5c 100644
--- a/src/fauxton/app/modules/pouchdb/base.js
+++ b/src/fauxton/app/modules/pouchdb/base.js
@@ -31,8 +31,8 @@ function(app, FauxtonAPI, MapReduce) {
var Pouch = {};
Pouch.MapReduce = MapReduce;
- Pouch.runViewQuery = function(fun, docs) {
- docs = [
+ Pouch.runViewQuery = function(fun, opts) {
+ /*docs = [
{_id: 'test_doc_1', foo: 'bar-1'},
{_id: 'test_doc_2', foo: 'bar-2'},
{_id: 'test_doc_3', foo: 'bar-3'},
@@ -43,15 +43,18 @@ function(app, FauxtonAPI, MapReduce) {
{_id: 'test_doc_8', foo: 'bar-8'},
{_id: 'test_doc_9', foo: 'bar-9'},
{_id: 'test_doc_10', foo: 'bar-10'}
- ];
+ ];*/
var deferred = FauxtonAPI.Deferred();
- var complete = function(resp) {
- console.log("COMPLETE TRIGGERED", arguments);
+ var complete = function(resp, rows) {
+ deferred.resolve(rows);
};
- return Pouch.MapReduce.query(fun, {docs: docs, complete:complete});
- };
+ var options = _.extend(opts, {complete: complete});
+ Pouch.MapReduce.query(fun, options);
+ return deferred;
+ };
+ //pdb.runViewQuery({map:function(doc) { emit(doc._id, doc.foo) }})
return Pouch;
});
diff --git a/src/fauxton/app/modules/pouchdb/pouchdb.mapreduce.js b/src/fauxton/app/modules/pouchdb/pouchdb.mapreduce.js
index b97eb7fc3..a2d0b91b5 100644
--- a/src/fauxton/app/modules/pouchdb/pouchdb.mapreduce.js
+++ b/src/fauxton/app/modules/pouchdb/pouchdb.mapreduce.js
@@ -39,6 +39,36 @@ function(app, FauxtonAPI, Collate) {
//var MapReduce = function(db) {
var MapReduce = function() {
+ var builtInReduce = {
+ "_sum": function(keys, values){
+ return sum(values);
+ },
+
+ "_count": function(keys, values, rereduce){
+ if (rereduce){
+ return sum(values);
+ } else {
+ return values.length;
+ }
+ },
+
+ "_stats": function(keys, values, rereduce){
+ return {
+ 'sum': sum(values),
+ 'min': Math.min.apply(null, values),
+ 'max': Math.max.apply(null, values),
+ 'count': values.length,
+ 'sumsqr': (function(){
+ _sumsqr = 0;
+ for(var idx in values){
+ _sumsqr += values[idx] * values[idx];
+ }
+ return _sumsqr;
+ })()
+ };
+ }
+ };
+
function viewQuery(fun, options) {
console.log("IN VIEW QUERY");
if (!options.complete) {
@@ -55,13 +85,13 @@ function(app, FauxtonAPI, Collate) {
var completed= false;
var emit = function(key, val) {
- console.log("IN EMIT: ", key, val, current);
+ //console.log("IN EMIT: ", key, val, current);
var viewRow = {
id: current.doc._id,
key: key,
value: val
};
- console.log("VIEW ROW: ", viewRow);
+ //console.log("VIEW ROW: ", viewRow);
if (options.startkey && Pouch.collate(key, options.startkey) < 0) return;
if (options.endkey && Pouch.collate(key, options.endkey) > 0) return;
@@ -95,7 +125,11 @@ function(app, FauxtonAPI, Collate) {
// ugly way to make sure references to 'emit' in map/reduce bind to the
// above emit
eval('fun.map = ' + fun.map.toString() + ';');
- if (fun.reduce) {
+ if (fun.reduce && options.reduce) {
+ if (builtInReduce[fun.reduce]) {
+ console.log('built in reduce');
+ fun.reduce = builtInReduce[fun.reduce];
+ }
eval('fun.reduce = ' + fun.reduce.toString() + ';');
}
@@ -105,6 +139,7 @@ function(app, FauxtonAPI, Collate) {
//only proceed once all documents are mapped and joined
var checkComplete= function(){
+ console.log('check');
if (completed && results.length == num_started){
results.sort(function(a, b) {
return Pouch.collate(a.key, b.key);
@@ -116,6 +151,7 @@ function(app, FauxtonAPI, Collate) {
return options.complete(null, {rows: results});
}
+ console.log('reducing', options);
var groups = [];
results.forEach(function(e) {
var last = groups[groups.length-1] || null;
@@ -130,19 +166,21 @@ function(app, FauxtonAPI, Collate) {
e.value = fun.reduce(e.key, e.value) || null;
e.key = e.key[0][0];
});
+ console.log('GROUPs', groups);
options.complete(null, {rows: groups});
}
};
if (options.docs) {
- console.log("RUNNING MR ON DOCS: ", options.docs);
+ //console.log("RUNNING MR ON DOCS: ", options.docs);
_.each(options.docs, function(doc) {
current = {doc: doc};
fun.map.call(this, doc);
}, this);
- return options.complete(null, {rows: results});
+ completed = true;
+ return checkComplete();//options.complete(null, {rows: results});
} else {
- console.log("COULD NOT FIND DOCS");
+ //console.log("COULD NOT FIND DOCS");
return false;
}
diff --git a/src/fauxton/app/templates/documents/all_docs_list.html b/src/fauxton/app/templates/documents/all_docs_list.html
index 2f63af038..9f4ffddfb 100644
--- a/src/fauxton/app/templates/documents/all_docs_list.html
+++ b/src/fauxton/app/templates/documents/all_docs_list.html
@@ -28,97 +28,10 @@ the License.
</div>
<% } %>
- <div class="row">
- <div class="all-docs-list errors-container"></div>
- <div id="edit-index-container"></div>
- <% if (viewList) { %>
- <div class="accordion" id="advanced-options-accordion">
- <div class="accordion-group">
- <div class="accordion-heading">
- <a class="accordion-toggle" data-bypass="true" data-toggle="collapse" data-parent="#advanced-options-accordion" href="#collapse-advanced-options">
- <i class="icon-plus"></i> Advanced Options
- </a>
- </div>
- <div id="collapse-advanced-options" class="accordion-body collapse">
- <div class="accordion-inner">
- <form class="view-query-update">
- <div class="controls controls-row">
- <label class="span3 inline">
- Limit:
- <select name="limit" class="input-small">
- <option>5</option>
- <option selected="selected">10</option>
- <option>25</option>
- <option>50</option>
- <option>100</option>
- </select>
- </label>
- <label class="span3 checkbox inline">
- <input name="include_docs" type="checkbox" value="true"> Include Docs
- </label>
- <% if (hasReduce) { %>
- <label class="span2 checkbox inline">
- <input name="reduce" type="checkbox" value="true"> Reduce
- </label>
- <label class="span4 inline">
- Group Level:
- <select disabled name="group_level" class="input-small">
- <option value="0">None</option>
- <option value="1">1</option>
- <option value="2">2</option>
- <option value="3">3</option>
- <option value="4">4</option>
- <option value="5">5</option>
- <option value="6">6</option>
- <option value="7">7</option>
- <option value="8">8</option>
- <option value="9">9</option>
- <option value="999" selected="selected">exact</option>
- </select>
- </label>
- <% } %>
- </div>
-
- <div class="controls controls-row">
- <input name="key" class="span4" type="text" placeholder="Key">
- <input name="keys" class="span8" type="text" placeholder="Keys">
- </div>
- <div class="controls controls-row">
- <input name="startkey" class="span6" type="text" placeholder="Start Key">
- <input name="endkey" class="span6" type="text" placeholder="End Key">
- </div>
- <div class="controls controls-row">
- <label class="span2 checkbox inline">
- <input name="stale" type="checkbox" value="ok"> Stale
- </label>
- <label class="span2 checkbox inline">
- <input name="descending" type="checkbox" value="true"> Descending
- </label>
- <label class="span4 checkbox inline">
- <input name="inclusive_end" type="checkbox" value="false"> Disable Inclusive End
- </label>
- <label class="span4 checkbox inline">
- <input name="update_seq" type="checkbox" value="true"> Include Update Sequence
- </label>
- </div>
- <div class="controls controls-row">
- <button type="submit" class="btn btn-primary">Query</button>
- </div>
- </form>
-
- </div>
- </div>
-
- </div>
- </div>
- <% } %>
- </div>
-
-
<p>
- Showing 1-<%= database.models.length %> of <%= database.totalRows() %> rows
- <% if (database.updateSeq()) { %>
- -- Update Sequence: <%= database.updateSeq() %>
+ Showing 1-<%= numModels %> of <%= totalRows %> rows
+ <% if (updateSeq) { %>
+ -- Update Sequence: <%= updateSeq %>
<% } %>
</p>
<table class="all-docs table table-striped table-condensed">
diff --git a/src/fauxton/app/templates/documents/changes.html b/src/fauxton/app/templates/documents/changes.html
index 3e5009c0f..e528a1c4a 100644
--- a/src/fauxton/app/templates/documents/changes.html
+++ b/src/fauxton/app/templates/documents/changes.html
@@ -28,7 +28,9 @@ the License.
<% } else { %>
<td> <a href="#<%= database.url('app') %>/<%= change.id %>"><%= change.id %></a> </td>
<% } %>
- <td> <%= JSON.stringify(change.changes) %> </td>
+ <td>
+ <pre class="prettyprint"> <%= JSON.stringify({changes: change.changes, doc: change.doc}, null, " ") %> </pre>
+ </td>
<td><%= change.deleted ? "true" : "false" %></td>
</tr>
<% }); %>
diff --git a/src/fauxton/app/templates/documents/view_editor.html b/src/fauxton/app/templates/documents/view_editor.html
index 4a8668e29..a34ff0dc3 100644
--- a/src/fauxton/app/templates/documents/view_editor.html
+++ b/src/fauxton/app/templates/documents/view_editor.html
@@ -11,82 +11,184 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
-->
+<div class="row">
+ <div class="all-docs-list errors-container"></div>
+ <div id="edit-index-container">
-<div class="accordion" id="edit-index-accordion">
- <div class="accordion-group">
- <div class="accordion-heading">
- <a class="accordion-toggle" data-bypass="true" data-toggle="collapse" data-parent="#edit-index-accordion" href="#collapse-edit-index">
- <i class="icon-wrench"></i> Edit Index
- </a>
- </div>
- <div id="collapse-edit-index" class="accordion-body collapse">
- <div class="accordion-inner">
- <div id="define-view" class="ddoc-alert well">
- <div class="errors-container"></div>
- <form class="form-horizontal">
- <h3>Define your index</h3>
- <div class="control-group">
- <label class="control-label" for="ddoc">Design document <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#design-docs"><i class="icon-question-sign"></i></a></label>
- <div class="controls">
- <select id="ddoc">
- <optgroup label="Select a document">
- <option>New document</option>
- <% ddocs.each(function(ddoc) { %>
- <% if (ddoc.id == "_design/"+viewCollection.design) { %>
- <option selected="selected"><%= ddoc.id %></option>
- <% } else { %>
- <option><%= ddoc.id %></option>
- <% } %>
- <% }); %>
- <option selected="selected">_design/views101</option>
- </optgroup>
- </select>
- </div>
+ <div class="accordion" id="edit-index-accordion">
+ <div class="accordion-group">
+ <div class="accordion-heading">
+ <a class="accordion-toggle" data-bypass="true" data-toggle="collapse" data-parent="#edit-index-accordion" href="#collapse-edit-index">
+ <i class="icon-wrench"></i> <% if (newView) { %> Create Index <% } else { %> Edit Index <% } %>
+ </a>
+ </div>
+ <div id="collapse-edit-index" class="accordion-body <% if (!newView) { %> collapse <% } %>">
+ <div class="accordion-inner">
+ <div id="define-view" class="ddoc-alert well">
+ <div class="errors-container"></div>
+ <form class="form-horizontal">
+ <h3>Define your index</h3>
+ <div class="control-group">
+ <div class="row" style="margin-left:10px">
+ <div class="span3">
+ <label class="control-label" for="ddoc">Design document <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#design-docs"><i class="icon-question-sign"></i></a></label>
+ <div class="controls">
+ <select id="ddoc">
+ <optgroup label="Select a document">
+ <option id="new-doc">New document</option>
+ <% ddocs.each(function(ddoc) { %>
+ <% if (ddoc.id === ddocName) { %>
+ <option selected="selected"><%= ddoc.id %></option>
+ <% } else { %>
+ <option><%= ddoc.id %></option>
+ <% } %>
+ <% }); %>
+ </optgroup>
+ </select>
+ </div>
+ </div>
+ <div id="new-ddoc-section" class="span5" style="display:none">
+ <label class="control-label" for="new-ddoc"> _design/ </label>
+ <div class="controls">
+ <input type="text" id="new-ddoc" placeholder="newDesignDoc">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="index-name">Index name <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#view-functions"><i class="icon-question-sign"></i></a></label>
+ <div class="controls">
+ <input type="text" id="index-name" value="<%= viewName %>" placeholder="Index name" />
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="map-function">Map function <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#map-functions"><i class="icon-question-sign"></i></a></label>
+ <div class="controls">
+ <% if (newView) { %>
+ <textarea class="js-editor" id="map-function"><%= langTemplates.map %></textarea>
+ <% } else { %>
+ <textarea class="js-editor" id="map-function"><%= ddoc.get('views')[viewName].map %></textarea>
+ <% } %>
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="reduce-function-selector">Reduce function <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs.html#reduce-and-rereduce-functions"><i class="icon-question-sign"></i></a></label>
+ <div class="controls">
+ <select id="reduce-function-selector">
+ <option value="" <%= !reduceFunStr ? 'selected="selected"' : '' %>>None</option>
+ <% _.each(["_sum", "_count", "_stats"], function(reduce) { %>
+ <option value="<%= reduce %>" <% if (reduce == reduceFunStr) { %>selected<% } %>><%= reduce %></option>
+ <% }) %>
+ <option value="CUSTOM" <% if (isCustomReduce) { %>selected<% } %>>Custom reduce</option>
+ </select>
+ <span class="help-block">Reduce functions are optional.</span>
+ </div>
+ </div>
+ <div class="control-group reduce-function">
+ <label class="control-label" for="reduce-function">Custom Reduce</label>
+ <div class="controls">
+ <% if (newView) { %>
+ <textarea class="js-editor" id="reduce-function"><%= langTemplates.reduce %></textarea>
+ <% } else { %>
+ <textarea class="js-editor" id="reduce-function"><%= ddoc.get('views')[viewName].reduce %></textarea>
+ <% } %>
+ </div>
+ </div>
+ <div class="control-group">
+ <hr />
+ <div class="controls">
+ <% if (!this.newView) { %>
+ <button class="btn btn-small btn-danger delete">Delete</button>
+ <% } %>
+ <button class="btn btn-small btn-info preview">Preview</button>
+ <button class="btn btn-primary save">Save</button>
+ </div>
+ </div>
+ <div class="clearfix"></div>
+ </form>
</div>
- <div class="control-group">
- <label class="control-label" for="index-name">Index name <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#view-functions"><i class="icon-question-sign"></i></a></label>
- <div class="controls">
- <input type="text" id="index-name" value="<%= viewCollection.view %>" placeholder="Index name" />
+ </div>
+ </div>
+
+ </div>
+ </div>
+ <div class="accordion" id="advanced-options-accordion">
+ <div class="accordion-group">
+ <div class="accordion-heading">
+ <a class="accordion-toggle" data-bypass="true" data-toggle="collapse" data-parent="#advanced-options-accordion" href="#collapse-advanced-options">
+ <i class="icon-plus"></i> Advanced Options
+ </a>
+ </div>
+ <div id="collapse-advanced-options" class="accordion-body collapse">
+ <div class="accordion-inner">
+ <form class="view-query-update">
+ <div class="controls controls-row">
+ <label class="span3 inline">
+ Limit:
+ <select name="limit" class="input-small">
+ <option>5</option>
+ <option selected="selected">10</option>
+ <option>25</option>
+ <option>50</option>
+ <option>100</option>
+ </select>
+ </label>
+ <label class="span3 checkbox inline">
+ <input name="include_docs" type="checkbox" value="true"> Include Docs
+ </label>
+ <% if (hasReduce) { %>
+ <label class="span2 checkbox inline">
+ <input name="reduce" type="checkbox" value="true"> Reduce
+ </label>
+ <label class="span4 inline">
+ Group Level:
+ <select disabled name="group_level" class="input-small">
+ <option value="0">None</option>
+ <option value="1">1</option>
+ <option value="2">2</option>
+ <option value="3">3</option>
+ <option value="4">4</option>
+ <option value="5">5</option>
+ <option value="6">6</option>
+ <option value="7">7</option>
+ <option value="8">8</option>
+ <option value="9">9</option>
+ <option value="999" selected="selected">exact</option>
+ </select>
+ </label>
+ <% } %>
</div>
- </div>
- <div class="control-group">
- <label class="control-label" for="map-function">Map function <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#map-functions"><i class="icon-question-sign"></i></a></label>
- <div class="controls">
- <textarea class="js-editor" id="map-function"><%= ddoc.get('doc').views[viewCollection.view].map %></textarea>
+
+ <div class="controls controls-row">
+ <input name="key" class="span4" type="text" placeholder="Key">
+ <input name="keys" class="span8" type="text" placeholder="Keys">
</div>
- </div>
- <div class="control-group">
- <label class="control-label" for="reduce-function-selector">Reduce function <a target="_couch_docs" href="http://docs.couchdb.org/en/latest/ddocs/#reduce-and-rereduce-functions"><i class="icon-question-sign"></i></a></label>
- <div class="controls">
- <select id="reduce-function-selector">
- <option value="" <%= !reduceFunStr ? 'selected="selected"' : '' %>>None</option>
- <% _.each(["_sum", "_count", "_stats"], function(reduce) { %>
- <option value="<%= reduce %>" <% if (reduce == reduceFunStr) { %>selected<% } %>><%= reduce %></option>
- <% }) %>
- <option value="CUSTOM" <% if (isCustomReduce) { %>selected<% } %>>Custom reduce</option>
- </select>
- <span class="help-block">Reduce functions are optional.</span>
+ <div class="controls controls-row">
+ <input name="startkey" class="span6" type="text" placeholder="Start Key">
+ <input name="endkey" class="span6" type="text" placeholder="End Key">
</div>
- </div>
- <div class="control-group reduce-function">
- <label class="control-label" for="reduce-function">Custom Reduce</label>
- <div class="controls">
- <textarea class="js-editor" id="reduce-function"><%= ddoc.get('doc').views[viewCollection.view].reduce %></textarea>
+ <div class="controls controls-row">
+ <label class="span2 checkbox inline">
+ <input name="stale" type="checkbox" value="ok"> Stale
+ </label>
+ <label class="span2 checkbox inline">
+ <input name="descending" type="checkbox" value="true"> Descending
+ </label>
+ <label class="span4 checkbox inline">
+ <input name="inclusive_end" type="checkbox" value="false"> Disable Inclusive End
+ </label>
+ <label class="span4 checkbox inline">
+ <input name="update_seq" type="checkbox" value="true"> Include Update Sequence
+ </label>
</div>
- </div>
- <div class="control-group">
- <hr />
- <div class="controls">
- <button class="btn btn-small btn-inverse cancel">Cancel</button>
- <button class="btn btn-small btn-info preview">Preview</button>
- <button class="btn btn-primary save">Save</button>
+ <div class="controls controls-row">
+ <button type="submit" class="btn btn-primary">Query</button>
</div>
- </div>
- <div class="clearfix"></div>
- </form>
+ </form>
+
+ </div>
</div>
+
</div>
</div>
-
</div>
-</div>
diff --git a/src/fauxton/app/templates/layouts/with_tabs_sidebar.html b/src/fauxton/app/templates/layouts/with_tabs_sidebar.html
index f78832fe4..0b5f2c7a1 100644
--- a/src/fauxton/app/templates/layouts/with_tabs_sidebar.html
+++ b/src/fauxton/app/templates/layouts/with_tabs_sidebar.html
@@ -21,7 +21,10 @@ the License.
<div class="row-fluid">
<div id="sidebar-content" class="sidebar span4"></div>
- <div id="dashboard-content" class="list span8 pull-right"></div>
+ <div id="dashboard-content" class="list span8 pull-right">
+ <div id="dashboard-upper-content"></div>
+ <div id="dashboard-lower-content"></div>
+ </div>
</div>
</div>