You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ns...@apache.org on 2013/12/14 12:41:50 UTC

[44/51] [partial] Bring Fauxton directories together

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/config/templates/dashboard.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/config/templates/dashboard.html b/share/www/fauxton/src/app/addons/config/templates/dashboard.html
new file mode 100644
index 0000000..ffbeb37
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/config/templates/dashboard.html
@@ -0,0 +1,52 @@
+<!--
+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.
+-->
+
+<div class="row">
+  <div class="span2 offset10">
+    <button id="add-section" href="#" class="button button-margin">
+      <i class="icon-plus icon-white"> </i>
+      Add Section
+    </button>
+  </div>
+</div>
+<table class="config table table-striped table-bordered">
+  <thead>
+    <th id="config-section"> Section </th>
+    <th id="config-option"> Option </th>
+    <th id="config-value"> Value </th>
+    <th id="config-trash"></th>
+  </thead>
+  <tbody>
+  </tbody>
+</table>
+<div id="add-section-modal" class="modal hide fade">
+  <div class="modal-header">
+    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+    <h3>Create Config Option</h3>
+  </div>
+  <div class="modal-body">
+    <form id="add-section-form" class="form well">
+      <label>Section</label>
+      <input type="text" name="section" placeholder="Section">
+      <span class="help-block">Enter an existing section name to add to it.</span>
+      <input type="text" name="name" placeholder="Name">
+      <br/>
+      <input type="text" name="value" placeholder="Value">
+      <div class="modal-footer">
+        <button type="button" class="btn" data-dismiss="modal">Cancel</button>
+        <button type="submit" class="btn btn-primary"> Save </button>
+      </div>
+    </form>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/config/templates/item.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/config/templates/item.html b/share/www/fauxton/src/app/addons/config/templates/item.html
new file mode 100644
index 0000000..3e6e4ee
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/config/templates/item.html
@@ -0,0 +1,31 @@
+<!--
+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.
+-->
+
+<% if (option.index === 0) {%>
+<th> <%= option.section %> </th>
+<% } else { %>
+<td></td>
+<% } %>
+<td> <%= option.name %> </td>
+<td>
+  <div id="show-value">
+    <%= option.value %> <button class="edit-button"> Edit </button>
+  </div>
+  <div id="edit-value-form" style="display:none">
+    <input class="value-input" type="text" value="<%= option.value %>" />
+    <button id="save-value" class="btn btn-success btn-small"> Save </button>
+    <button id="cancel-value" class="btn btn-danger btn-small"> Cancel </button>
+  </div>
+</td>
+<td id="delete-value"> <i class="icon-trash"> </i> </td>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/contribute/base.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/contribute/base.js b/share/www/fauxton/src/app/addons/contribute/base.js
new file mode 100644
index 0000000..8f622fe
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/contribute/base.js
@@ -0,0 +1,33 @@
+// 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.
+
+define([
+  // Libraries.
+  "jquery",
+  "lodash"
+],
+function($, _){
+  $.contribute = function(message, file){
+    /*
+    var JST = window.JST = window.JST || {};
+    var template = JST['app/addons/contribute/templates/modal.html'];
+    console.log(template);
+    var compiled = template({message: message, file: file});
+    */
+    console.log('contribute!contribute!monorail!contribute!');
+    /*
+    console.log(compiled);
+    var elem = $(compiled);
+    elem.modal('show');
+    */
+  };
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/exampleAuth/base.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/exampleAuth/base.js b/share/www/fauxton/src/app/addons/exampleAuth/base.js
new file mode 100644
index 0000000..aa99670
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/exampleAuth/base.js
@@ -0,0 +1,59 @@
+// 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.
+
+define([
+  "app",
+
+  "api"
+],
+
+function(app, FauxtonAPI) {
+  // This is an example module of using the new auth module.
+
+  var noAccessView = FauxtonAPI.View.extend({
+    template: "addons/exampleAuth/templates/noAccess"
+
+  });
+
+  // To utilise the authentication - all that is required, is one callback
+  // that is registered with the auth api. This function can return an array
+  // of deferred objects.
+  // The roles argument that is passed in is the required roles for the current user
+  // to be allowed to access the current page.
+  // The layout is the main layout for use when you want to render a view onto the page
+  var auth = function (roles) {
+    var deferred = $.Deferred();
+
+    if (roles.indexOf('_admin') > -1) {
+      deferred.reject();
+    } else {
+      deferred.resolve();
+    }
+
+    return [deferred];
+  };
+
+  // If you would like to do something with when access is denied you can register this callback.
+  // It will be called is access has been denied on the previous page.
+  var authFail = function () {
+    app.masterLayout.setView('#dashboard', new noAccessView());
+    app.masterLayout.renderView('#dashboard');
+  };
+
+  // Register the auth call back. This will be called before new route rendered
+  FauxtonAPI.auth.registerAuth(auth);
+  // Register a failed route request callback. This is called if access is denied.
+  FauxtonAPI.auth.registerAuthDenied(authFail);
+
+
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/exampleAuth/templates/noAccess.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/exampleAuth/templates/noAccess.html b/share/www/fauxton/src/app/addons/exampleAuth/templates/noAccess.html
new file mode 100644
index 0000000..f1a9506
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/exampleAuth/templates/noAccess.html
@@ -0,0 +1,19 @@
+<!--
+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.
+-->
+
+<div class="row-fluid" >
+  <div class="span6 offset4">
+  <h3> You do not have permission to view this page </h3>
+</div>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/logs/base.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/logs/base.js b/share/www/fauxton/src/app/addons/logs/base.js
new file mode 100644
index 0000000..1aecbdf
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/logs/base.js
@@ -0,0 +1,28 @@
+// 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.
+
+define([
+  "app",
+
+  "api",
+
+  // Modules
+  "addons/logs/routes"
+],
+
+function(app, FauxtonAPI, Log) {
+  Log.initialize = function() {
+    FauxtonAPI.addHeaderLink({title: "Log", href: "#_log", icon: "fonticon-log", className: 'logs'});
+  };
+
+  return Log;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/logs/resources.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/logs/resources.js b/share/www/fauxton/src/app/addons/logs/resources.js
new file mode 100644
index 0000000..3a47b92
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/logs/resources.js
@@ -0,0 +1,225 @@
+// 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.
+
+define([
+  "app",
+  "api",
+  "backbone"
+],
+
+function (app, FauxtonAPI, Backbone) {
+
+  var Log = FauxtonAPI.addon();
+
+  Log.Model = Backbone.Model.extend({
+
+    date: function () {
+      var date = new Date(this.get('date'));
+
+      var formatted_time = date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
+      var formatted_date = date.toDateString().slice(4, 10);
+
+      return formatted_date + ' ' + formatted_time;
+    },
+
+    logLevel: function () {
+      return this.get('log_level').replace(/ /g,'');
+    },
+
+    pid: function () {
+      return _.escape(this.get('pid'));
+    },
+
+    args: function () {
+      return _.escape(this.get('args'));
+    }
+
+  });
+
+  Log.Collection = Backbone.Collection.extend({
+    model: Log.Model,
+
+    initialize: function (options) {
+      this.params = {bytes: 5000};
+    },
+    
+    documentation: "log",
+
+    url: function () {
+      query = "?" + $.param(this.params);
+      return app.host + '/_log' + query;
+    },
+
+    // override fetch because backbone expects json and couchdb sends text/html for logs,
+    // I think its more elegant to set the dataType here than where ever fetch is called
+    fetch: function (options) {
+      options = options ? options : {};
+
+      return Backbone.Collection.prototype.fetch.call(this, _.extend(options, {dataType: "html"}));
+    },
+
+    parse: function (resp) {
+      var lines =  resp.split(/\n/);
+      return _.foldr(lines, function (acc, logLine) {
+        var match = logLine.match(/^\[(.*?)\]\s\[(.*?)\]\s\[(.*?)\]\s(.*)/);
+
+        if (!match) { return acc;}
+
+        acc.push({
+                  date: match[1],
+                  log_level: match[2],
+                  pid: match[3],
+                  args: match[4]
+                 });
+
+        return acc;
+      }, []);
+    }
+  });
+
+  Log.events = {};
+  _.extend(Log.events, Backbone.Events);
+
+  Log.Views.View = FauxtonAPI.View.extend({
+    template: "addons/logs/templates/dashboard",
+
+    initialize: function (options) {
+      this.refreshTime = options.refreshTime || 5000;
+
+      Log.events.on("log:filter", this.filterLogs, this);
+      Log.events.on("log:remove", this.removeFilterLogs, this);
+
+      this.filters = [];
+
+      this.collection.on("add", function () {
+        this.render();
+      }, this);
+    },
+
+    establish: function () {
+      return [this.collection.fetch()];
+    },
+
+    serialize: function () {
+      return { logs: new Log.Collection(this.createFilteredCollection())};
+    },
+
+    afterRender: function () {
+      this.startRefreshInterval();
+    },
+
+    cleanup: function () {
+      this.stopRefreshInterval();
+    },
+
+    filterLogs: function (filter) {
+      this.filters.push(filter);
+      this.render();
+    },
+
+    createFilteredCollection: function () {
+      var that = this;
+
+      return _.reduce(this.filters, function (logs, filter) {
+
+        return _.filter(logs, function (log) {
+          var match = false;
+
+          _.each(log, function (value) {
+            if (value.toString().match(new RegExp(filter))) {
+              match = true;
+            }
+          });
+          return match;
+        });
+
+
+      }, this.collection.toJSON(), this);
+
+    },
+
+    removeFilterLogs: function (filter) {
+      this.filters.splice(this.filters.indexOf(filter), 1);
+      this.render();
+    },
+
+    startRefreshInterval: function () {
+      var collection = this.collection;
+
+      // Interval already set
+      if (this.intervalId) { return ; }
+
+      this.intervalId = setInterval(function () {
+        collection.fetch();
+      }, this.refreshTime);
+
+    },
+
+    stopRefreshInterval: function () {
+      clearInterval(this.intervalId);
+    }
+  });
+
+  Log.Views.FilterView = FauxtonAPI.View.extend({
+    template: "addons/logs/templates/sidebar",
+
+    events: {
+      "submit #log-filter-form": "filterLogs"
+    },
+
+    filterLogs: function (event) {
+      event.preventDefault();
+      var $filter = this.$('input[name="filter"]'),
+          filter = $filter.val();
+
+      Log.events.trigger("log:filter", filter);
+
+      this.insertView("#filter-list", new Log.Views.FilterItemView({
+        filter: filter
+      })).render();
+
+      $filter.val('');
+    }
+
+  });
+
+  Log.Views.FilterItemView = FauxtonAPI.View.extend({
+    template: "addons/logs/templates/filterItem",
+    tagName: "li",
+
+    initialize: function (options) {
+      this.filter = options.filter;
+    },
+
+    events: {
+      "click .remove-filter": "removeFilter"
+    },
+
+    serialize: function () {
+      return {
+        filter: this.filter
+      };
+    },
+
+    removeFilter: function (event) {
+      event.preventDefault();
+
+      Log.events.trigger("log:remove", this.filter);
+      this.remove();
+    }
+
+  });
+
+
+  return Log;
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/logs/routes.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/logs/routes.js b/share/www/fauxton/src/app/addons/logs/routes.js
new file mode 100644
index 0000000..5c937af
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/logs/routes.js
@@ -0,0 +1,58 @@
+// 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.
+
+define([
+       "app",
+
+       "api",
+
+       // Modules
+       "addons/logs/resources"
+],
+
+function(app, FauxtonAPI, Log) {
+
+  var  LogRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "with_sidebar",
+
+    crumbs: [
+      {"name": "Logs", "link": "_log"}
+    ],
+
+    routes: {
+      "_log": "showLog"
+    },
+
+    selectedHeader: "Log",
+
+    roles: ["_admin"],
+
+    apiUrl: function() {
+      return [this.logs.url(), this.logs.documentation];
+    },
+
+    initialize: function () {
+      this.logs = new Log.Collection();
+      this.setView("#sidebar-content", new Log.Views.FilterView({}));
+    },
+
+    showLog: function () {
+      this.setView("#dashboard-content", new Log.Views.View({collection: this.logs}));
+    }
+  });
+
+  Log.RouteObjects = [LogRouteObject];
+
+  return Log;
+
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/logs/templates/dashboard.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/logs/templates/dashboard.html b/share/www/fauxton/src/app/addons/logs/templates/dashboard.html
new file mode 100644
index 0000000..199794c
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/logs/templates/dashboard.html
@@ -0,0 +1,46 @@
+<!--
+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.
+-->
+
+ <h2> CouchDB Logs </h2>
+  <table class="table table-bordered" >
+  <thead>
+    <tr>
+      <th class="Date">Date</th>
+      <th class="Log Level">Log Value</th>
+      <th class="Pid">Pid</th>
+      <th class="Args">Url</th>
+    </tr>
+  </thead>
+
+  <tbody>
+    <% logs.each(function (log) { %>
+    <tr class="<%= log.logLevel() %>">
+      <td>
+        <!-- TODO: better format the date -->
+        <%= log.date() %>
+      </td>
+      <td>
+        <%= log.logLevel() %>
+      </td>
+      <td>
+        <%= log.pid() %>
+      </td>
+      <td>
+        <!-- TODO: split the line, maybe put method in it's own column -->
+        <%= log.args() %>
+      </td>
+    </tr>
+    <% }); %>
+  </tbody>
+</table>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/logs/templates/filterItem.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/logs/templates/filterItem.html b/share/www/fauxton/src/app/addons/logs/templates/filterItem.html
new file mode 100644
index 0000000..c4e885a
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/logs/templates/filterItem.html
@@ -0,0 +1,16 @@
+<!--
+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.
+-->
+
+<span class="label label-info"> <%= filter %>  </span>
+<a class="label label-info remove-filter" href="#">&times;</a>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/logs/templates/sidebar.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/logs/templates/sidebar.html b/share/www/fauxton/src/app/addons/logs/templates/sidebar.html
new file mode 100644
index 0000000..59b10ac
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/logs/templates/sidebar.html
@@ -0,0 +1,27 @@
+<!--
+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.
+-->
+
+<div id="log-sidebar">
+  <header>Log Filter</header>
+  <form class="form-inline" id="log-filter-form">
+    <fieldset>
+      <input type="text" name="filter" placeholder="Type a filter to sort the logs by">
+      <!-- TODO: filter by method -->
+      <!-- TODO: correct removed filter behaviour -->
+      <button type="submit" class="btn">Filter</button>
+      <span class="help-block"> <h6> Eg. debug or <1.4.1> or any regex </h6> </span>
+    </fieldset>
+  </form>
+  <ul id="filter-list"></ul>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/logs/tests/logSpec.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/logs/tests/logSpec.js b/share/www/fauxton/src/app/addons/logs/tests/logSpec.js
new file mode 100644
index 0000000..621cc9b
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/logs/tests/logSpec.js
@@ -0,0 +1,38 @@
+// 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.
+define([
+       'addons/logs/base',
+       'chai'
+], function (Log, chai) {
+  var expect = chai.expect;
+
+  describe('Logs Addon', function(){
+
+    describe('Log Model', function () {
+      var log;
+
+      beforeEach(function () {
+        log = new Log.Model({
+          log_level: 'DEBUG',
+          pid: '1234',
+          args: 'testing 123',
+          date: (new Date()).toString()
+        });
+      });
+
+      it('should have a log level', function () {
+        expect(log.logLevel()).to.equal('DEBUG');
+      });
+
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/permissions/assets/less/permissions.less
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/permissions/assets/less/permissions.less b/share/www/fauxton/src/app/addons/permissions/assets/less/permissions.less
new file mode 100644
index 0000000..7ce4d10
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/permissions/assets/less/permissions.less
@@ -0,0 +1,44 @@
+// 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.
+
+.border-hdr {
+  border-bottom: 1px solid #E3E3E3;
+  margin-bottom: 10px;
+  .help{
+
+  }
+  h3{
+    text-transform: capitalize;
+    margin-bottom: 0;
+  }
+}
+
+
+.permission-items.unstyled{
+	margin-left: 0px;
+	li {
+		padding: 5px;
+		border-bottom: 1px solid #E3E3E3;
+		border-right: 1px solid #E3E3E3;
+		border-left: 3px solid #E3E3E3;
+		&:first-child{
+			border-top: 1px solid #E3E3E3;
+		}
+		&:nth-child(odd){
+      border-left: 3px solid red;
+    }
+    button {
+    	float: right;
+    	margin-bottom: 6px;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/permissions/base.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/permissions/base.js b/share/www/fauxton/src/app/addons/permissions/base.js
new file mode 100644
index 0000000..016ba1c
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/permissions/base.js
@@ -0,0 +1,25 @@
+// 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.
+
+define([
+       "app",
+       "api",
+       "addons/permissions/routes"
+],
+
+function(app, FauxtonAPI, Permissions) {
+
+  Permissions.initialize = function() {};
+
+  return Permissions;
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/permissions/resources.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/permissions/resources.js b/share/www/fauxton/src/app/addons/permissions/resources.js
new file mode 100644
index 0000000..66eaffd
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/permissions/resources.js
@@ -0,0 +1,70 @@
+// 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.
+
+define([
+       "app",
+       "api"
+],
+function (app, FauxtonAPI ) {
+  var Permissions = FauxtonAPI.addon();
+
+  Permissions.Security = Backbone.Model.extend({
+    defaults: {
+      admins: {
+        names: [],
+        roles: []
+      },
+
+      members: {
+        names: [],
+        roles: []
+      }
+    },
+
+    isNew: function () {
+      return false;
+    },
+
+    initialize: function (attrs, options) {
+      this.database = options.database;
+    },
+
+    url: function () {
+      return this.database.id + '/_security';
+    },
+
+    addItem: function (value, type, section) {
+      var sectionValues = this.get(section);
+
+      if (!sectionValues || !sectionValues[type]) { 
+        return {
+          error: true,
+          msg: 'Section ' + section + 'does not exist'
+        };
+      }
+
+      if (sectionValues[type].indexOf(value) > -1) { 
+        return {
+          error: true,
+          msg: 'Role/Name has already been added'
+        }; 
+      }
+
+      sectionValues[type].push(value);
+      return this.set(section, sectionValues);
+    }
+
+  });
+
+  return Permissions;
+});
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/permissions/routes.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/permissions/routes.js b/share/www/fauxton/src/app/addons/permissions/routes.js
new file mode 100644
index 0000000..89c2bd7
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/permissions/routes.js
@@ -0,0 +1,63 @@
+// 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.
+
+define([
+       "app",
+       "api",
+       "modules/databases/base",
+       "addons/permissions/views"
+],
+function (app, FauxtonAPI, Databases, Permissions) {
+  
+  var PermissionsRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: 'one_pane',
+    selectedHeader: 'Databases',
+
+    routes: {
+      'database/:database/permissions': 'permissions'
+    },
+
+    initialize: function (route, masterLayout, options) {
+      var docOptions = app.getParams();
+      docOptions.include_docs = true;
+
+      this.databaseName = options[0];
+      this.database = new Databases.Model({id:this.databaseName});
+      this.security = new Permissions.Security(null, {
+        database: this.database
+      });
+    },
+
+    establish: function () {
+      return [this.database.fetch(), this.security.fetch()];
+    },
+
+    permissions: function () {
+      this.setView('#dashboard-content', new Permissions.Permissions({
+        database: this.database,
+        model: this.security
+      }));
+
+    },
+
+    crumbs: function () {
+      return [
+        {"name": this.database.id, "link": Databases.databaseUrl(this.database)},
+        {"name": "Permissions", "link": "/permissions"}
+      ];
+    },
+
+  });
+  
+  Permissions.RouteObjects = [PermissionsRouteObject];
+  return Permissions;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/permissions/templates/item.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/permissions/templates/item.html b/share/www/fauxton/src/app/addons/permissions/templates/item.html
new file mode 100644
index 0000000..616ffd6
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/permissions/templates/item.html
@@ -0,0 +1,17 @@
+<!--
+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.
+-->
+
+<span> <%= item %> </span>
+<button type="button" class="close">&times;</button>
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/permissions/templates/permissions.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/permissions/templates/permissions.html b/share/www/fauxton/src/app/addons/permissions/templates/permissions.html
new file mode 100644
index 0000000..99c9ff5
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/permissions/templates/permissions.html
@@ -0,0 +1,15 @@
+<!--
+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.
+-->
+
+<div id="sections"> </div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/permissions/templates/section.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/permissions/templates/section.html b/share/www/fauxton/src/app/addons/permissions/templates/section.html
new file mode 100644
index 0000000..3360308
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/permissions/templates/section.html
@@ -0,0 +1,46 @@
+<!--
+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.
+-->
+<header class="border-hdr">
+<h3> <%= (section) %> </h3>
+<p id="help"> <%= help %> <a href="<%=getDocUrl('database_permission')%>" target="_blank"><i class="icon-question-sign"> </i> </a></p>
+</header>
+
+<div class="row">
+  <div class="span6">
+    <header>
+      <h4> Users </h4>
+      <p>Specify users who will have <%= section %> access to this database.</p>
+    </header>
+    <form class="permission-item-form form-inline">
+      <input data-section="<%= section %>" data-type="names" type="text" class="item input-small" placeholder="Add Name">
+      <button type="submit" class="button btn green fonticon-circle-plus">Add Name</button>
+    </form>
+    <ul class="clearfix unstyled permission-items span10" id="<%= (section) %>-items-names">
+    </ul>
+  </div>
+  <div class="span6">
+    <header>
+      <h4> Roles </h4>
+      <p>All users under the following role(s) will have <%= section %> access.</p>
+    </header>
+
+
+    <form class="permission-item-form form-inline">
+      <input data-section="<%= section %>" data-type="roles" type="text" class="item input-small" placeholder="Add Role">
+      <button type="submit" class="button btn green fonticon-circle-plus">Add Role</button>
+    </form>
+    <ul class="unstyled permission-items span10" id="<%= (section) %>-items-roles">
+    </ul>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/permissions/tests/resourceSpec.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/permissions/tests/resourceSpec.js b/share/www/fauxton/src/app/addons/permissions/tests/resourceSpec.js
new file mode 100644
index 0000000..f73687a
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/permissions/tests/resourceSpec.js
@@ -0,0 +1,51 @@
+// 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.
+define([
+       'api',
+       'addons/permissions/resources',
+      'testUtils'
+], function (FauxtonAPI, Models, testUtils) {
+  var assert = testUtils.assert;
+
+  describe('Permissions', function () {
+
+    describe('#addItem', function () {
+      var security;
+      
+      beforeEach(function () {
+        security = new Models.Security(null, {database: 'fakedb'});
+      });
+
+      it('Should add value to section', function () {
+
+        security.addItem('_user', 'names', 'admins');
+        assert.equal(security.get('admins').names[0], '_user');
+      });
+
+      it('Should handle incorrect type', function () {
+        security.addItem('_user', 'asdasd', 'admins');
+      });
+
+      it('Should handle incorrect section', function () {
+        security.addItem('_user', 'names', 'Asdasd');
+      });
+
+      it('Should reject duplicates', function () {
+        security.addItem('_user', 'names', 'admins');
+        security.addItem('_user', 'names', 'admins');
+        assert.equal(security.get('admins').names.length, 1);
+      });
+    });
+
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/permissions/tests/viewsSpec.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/permissions/tests/viewsSpec.js b/share/www/fauxton/src/app/addons/permissions/tests/viewsSpec.js
new file mode 100644
index 0000000..e5330c0
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/permissions/tests/viewsSpec.js
@@ -0,0 +1,159 @@
+// 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.
+define([
+       'api',
+       'addons/permissions/views',
+       'addons/permissions/resources',
+       'testUtils'
+], function (FauxtonAPI, Views, Models, testUtils) {
+  var assert = testUtils.assert,
+  ViewSandbox = testUtils.ViewSandbox;
+
+  describe('Permission View', function () {
+
+    beforeEach(function () {
+      security = new Models.Security({'admins': {
+        'names': ['_user'],
+        'roles': []
+      }
+      }, {database: 'fakedb'});
+
+      section = new Views.Permissions({
+        database: 'fakedb',
+        model: security
+      });
+
+      viewSandbox = new ViewSandbox();
+      viewSandbox.renderView(section); 
+    });
+
+    afterEach(function () {
+      viewSandbox.remove();
+    });
+
+    describe('itemRemoved', function () {
+
+      it('Should set model', function () {
+        var saveMock = sinon.spy(security, 'set');
+        Views.events.trigger('itemRemoved');
+
+        assert.ok(saveMock.calledOnce);
+        var args = saveMock.args; 
+        assert.deepEqual(args[0][0], {"admins":{"names":["_user"],"roles":[]},"members":{"names":[],"roles":[]}});
+      });
+
+      it('Should save model', function () {
+        var saveMock = sinon.spy(security, 'save');
+        Views.events.trigger('itemRemoved');
+
+        assert.ok(saveMock.calledOnce);
+      });
+    });
+
+  });
+
+  describe('PermissionsSection', function () {
+    var section, security;
+
+    beforeEach(function () {
+      security = new Models.Security({'admins': {
+        'names': ['_user'],
+        'roles': []
+      }
+      }, {database: 'fakedb'});
+
+      section = new Views.PermissionSection({
+        section: 'admins',
+        model: security
+      });
+
+      viewSandbox = new ViewSandbox();
+      viewSandbox.renderView(section); 
+    });
+
+    afterEach(function () {
+      viewSandbox.remove();
+    });
+
+    describe('#discardRemovedViews', function () {
+      it('Should not filter out active views', function () {
+        section.discardRemovedViews();
+
+        assert.equal(section.nameViews.length, 1);
+
+      });
+
+      it('Should filter out removed views', function () {
+        section.nameViews[0].removed = true;
+        section.discardRemovedViews();
+
+        assert.equal(section.nameViews.length, 0);
+
+      });
+
+    });
+
+    describe('#getItemFromView', function () {
+
+      it('Should return item list', function () {
+        var items = section.getItemFromView(section.nameViews);
+
+        assert.deepEqual(items, ['_user']);
+      });
+
+    });
+
+    describe('#addItems', function () {
+
+      it('Should add item to model', function () {
+        //todo add a test here
+
+      });
+
+    });
+
+  });
+
+  describe('PermissionItem', function () {
+    var item;
+
+    beforeEach(function () {
+      item = new Views.PermissionItem({
+        item: '_user'
+      });
+
+      viewSandbox = new ViewSandbox();
+      viewSandbox.renderView(item); 
+    });
+
+    afterEach(function () {
+      viewSandbox.remove();
+    });
+
+    it('should trigger event on remove item', function () {
+      var eventSpy = sinon.spy();
+
+      Views.events.on('itemRemoved', eventSpy);
+
+      item.$('.close').click();
+      
+      assert.ok(eventSpy.calledOnce); 
+    });
+
+    it('should set removed to true', function () {
+      item.$('.close').click();
+      
+      assert.ok(item.removed); 
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/permissions/views.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/permissions/views.js b/share/www/fauxton/src/app/addons/permissions/views.js
new file mode 100644
index 0000000..eb5a378
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/permissions/views.js
@@ -0,0 +1,200 @@
+// 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.
+
+define([
+       "app",
+       "api",
+       "addons/permissions/resources"
+],
+function (app, FauxtonAPI, Permissions ) {
+  var events = {};
+  Permissions.events = _.extend(events, Backbone.Events);
+
+  Permissions.Permissions = FauxtonAPI.View.extend({
+    template: "addons/permissions/templates/permissions",
+
+    initialize: function (options) {
+      this.database = options.database;
+      this.listenTo(Permissions.events, 'itemRemoved', this.itemRemoved);
+    },
+
+    itemRemoved: function (event) {
+      this.model.set({
+        admins: this.adminsView.items(),
+        members: this.membersView.items()
+      });
+
+      this.model.save().then(function () {
+        FauxtonAPI.addNotification({
+          msg: 'Database permissions has been updated.'
+        });
+        }, function (xhr) {
+        FauxtonAPI.addNotification({
+          msg: 'Could not update permissions - reason: ' + xhr.responseText,
+          type: 'error'
+        });
+      });
+    },
+
+    beforeRender: function () {
+      this.adminsView = this.insertView('#sections', new Permissions.PermissionSection({
+        model: this.model,
+        section: 'admins',
+        help: 'Database admins can update design documents and edit the admin and member lists.'
+      }));
+
+      this.membersView = this.insertView('#sections', new Permissions.PermissionSection({
+        model: this.model,
+        section: 'members',
+        help: 'Database members can access the database. If no members are defined, the database is public.'
+      }));
+    },
+
+    serialize: function () {
+      return {
+        databaseName: this.database.id,
+      };
+    }
+  });
+
+  Permissions.PermissionSection = FauxtonAPI.View.extend({
+    template: "addons/permissions/templates/section",
+    initialize: function (options) {
+      this.section = options.section;
+      this.help = options.help;
+    },
+
+    events: {
+      "submit .permission-item-form": "addItem",
+      'click button.close': "removeItem"
+    },
+
+    beforeRender: function () {
+      var section = this.model.get(this.section);
+      
+      this.nameViews = [];
+      this.roleViews = [];
+
+      _.each(section.names, function (name) {
+        var nameView = this.insertView('#'+this.section+'-items-names', new Permissions.PermissionItem({
+          item: name,
+        }));
+        this.nameViews.push(nameView);
+      }, this);
+
+      _.each(section.roles, function (role) {
+        var roleView = this.insertView('#'+this.section+'-items-roles', new Permissions.PermissionItem({
+          item: role,
+        }));
+        this.roleViews.push(roleView);
+      }, this);
+    },
+
+    getItemFromView: function (viewList) {
+      return _.map(viewList, function (view) {
+        return view.item;
+      });
+    },
+
+    discardRemovedViews: function () {
+      this.nameViews = _.filter(this.nameViews, function (view) {
+        return !view.removed; 
+      });
+
+      this.roleViews = _.filter(this.roleViews, function (view) {
+        return !view.removed; 
+      });
+    },
+
+    items: function () {
+      this.discardRemovedViews();
+
+      return  {
+        names: this.getItemFromView(this.nameViews),
+        roles: this.getItemFromView(this.roleViews)
+      };
+    },
+
+    addItem: function (event) {
+      event.preventDefault();
+      var $item = this.$(event.currentTarget).find('.item'),
+          value = $item.val(),
+          section = $item.data('section'),
+          type = $item.data('type'),
+          that = this;
+
+      var resp = this.model.addItem(value, type, section);
+
+      if (resp && resp.error) {
+        return FauxtonAPI.addNotification({
+          msg: resp.msg,
+          type: 'error'
+        });
+      }
+
+      this.model.save().then(function () {
+        that.render();
+        FauxtonAPI.addNotification({
+          msg: 'Database permissions has been updated.'
+        });
+      }, function (xhr) {
+        FauxtonAPI.addNotification({
+          msg: 'Could not update permissions - reason: ' + xhr.responseText,
+          type: 'error'
+        });
+      });
+    },
+
+    serialize: function () {
+      return {
+        section: this.section,
+        help: this.help
+      };
+    }
+
+  });
+
+  Permissions.PermissionItem = FauxtonAPI.View.extend({
+    tagName: "li",
+    template: "addons/permissions/templates/item",
+    initialize: function (options) {
+      this.item = options.item;
+      this.viewsList = options.viewsList;
+    },
+
+    events: {
+      'click .close': "removeItem"
+    },
+
+    removeItem: function (event) {
+      var that = this;
+      event.preventDefault();
+      
+      this.removed = true;
+      Permissions.events.trigger('itemRemoved');
+
+      this.$el.hide('fast', function () {
+        that.remove();
+      });
+    },
+
+
+    serialize: function () {
+      return {
+        item: this.item
+      };
+    }
+
+  });
+
+  return Permissions;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/plugins/base.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/plugins/base.js b/share/www/fauxton/src/app/addons/plugins/base.js
new file mode 100644
index 0000000..0798fbd
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/plugins/base.js
@@ -0,0 +1,24 @@
+// 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.
+
+define([
+  "app",
+  "api",
+  "addons/plugins/routes"
+],
+
+function(app, FauxtonAPI, plugins) {
+  plugins.initialize = function() {
+    //FauxtonAPI.addHeaderLink({title: "Plugins", href: "#_plugins", icon: "fonticon-plugins", className: 'plugins'});
+  };
+  return plugins;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/plugins/resources.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/plugins/resources.js b/share/www/fauxton/src/app/addons/plugins/resources.js
new file mode 100644
index 0000000..d00fada
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/plugins/resources.js
@@ -0,0 +1,26 @@
+// 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.
+
+define([
+  "app",
+  "api"
+],
+
+function (app, FauxtonAPI) {
+  var plugins = FauxtonAPI.addon();
+
+  plugins.Hello = FauxtonAPI.View.extend({
+    template: "addons/plugins/templates/plugins"
+  });
+
+  return plugins;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/plugins/routes.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/plugins/routes.js b/share/www/fauxton/src/app/addons/plugins/routes.js
new file mode 100644
index 0000000..24d47f0
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/plugins/routes.js
@@ -0,0 +1,47 @@
+// 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.
+
+define([
+  "app",
+  "api",
+  "addons/plugins/resources"
+],
+function(app, FauxtonAPI, plugins) {
+      var  PluginsRouteObject = FauxtonAPI.RouteObject.extend({
+        layout: "one_pane",
+
+        crumbs: [
+          {"name": "Plugins","link": "_plugins"}
+        ],
+
+        routes: {
+           "_plugins": "pluginsRoute"
+        },
+
+        selectedHeader: "Plugins",
+
+        roles: ["_admin"],
+
+        apiUrl:'plugins',
+
+        initialize: function () {
+            //put common views used on all your routes here (eg:  sidebars )
+        },
+
+        pluginsRoute: function () {
+          this.setView("#dashboard-content", new plugins.Hello({}));
+        }
+      });
+
+      plugins.RouteObjects = [PluginsRouteObject];
+  return plugins;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/plugins/templates/plugins.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/plugins/templates/plugins.html b/share/www/fauxton/src/app/addons/plugins/templates/plugins.html
new file mode 100644
index 0000000..3002247
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/plugins/templates/plugins.html
@@ -0,0 +1,102 @@
+<!--
+
+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.
+
+-->
+    <div id="content">
+      <div class="row">
+        <h2>GeoCouch</h2>
+        <p>Version: <strong>couchdb1.2.x_v0.3.0-11-g66e6219</strong></p>
+        <p>Author: Volker Mische</p>
+        <p>
+          Available Erlang Versions:
+          <ul>
+            <li>CouchDB 1.4.0-XXX R15B01</li>
+          </ul>
+        </p>
+        <p>
+          <button href="#" class="install-plugin" data-url="http://people.apache.org/~jan" data-checksums='{"1.4.0": {"R15B03":"D5QPhrJTAifM42DXqAj4RxzfEtI="}}' data-name="geocouch" data-version="couchdb1.2.x_v0.3.0-16-g66e6219">Install GeoCouch Now</button>
+        </p>
+      </div>
+      <div class="row">
+        <h2>CouchPerUser</h2>
+        <p>Version: <strong>1.0.0</strong></p>
+        <p>Author: Bob Ippolito</p>
+        <p>
+          Available Erlang Versions:
+          <ul>
+            <li>CouchDB 1.4.0-XXX R15B01</li>
+          </ul>
+        </p>
+        <p>
+          <button href="#" class="install-plugin" data-url="http://people.apache.org/~jan" data-checksums='{"1.4.0": {"R15B03":"Aj3mjC6M75NA62q5/xkP0tl8Hws="}}' data-name="couchperuser" data-version="1.0.0">Install CouchPerUser Now</button>
+        </p>
+      </div>
+    </div>
+  </div></body>
+  <script>
+    $('.install-plugin').each(function() {
+      var button = $(this);
+      var name = button.data('name');
+      var version = button.data('version');
+      $.get("/_config/plugins/" + name + "/", function(body, textStatus) {
+        body = JSON.parse(body);
+        if(body == version) {
+          button.html('Already Installed. Click to Uninstall');
+          button.data('delete', true);
+        } else {
+          button.html('Other Version Installed: ' + body);
+          button.attr('disabled', true);
+        }
+      });
+    });
+
+    $('.install-plugin').click(function(event) {
+      var button = $(this);
+      var delete_plugin = button.data('delete') || false;
+      var plugin_spec = JSON.stringify({
+        name: button.data('name'),
+        url: button.data('url'),
+        version: button.data('version'),
+        checksums: button.data('checksums'),
+        "delete": delete_plugin
+      });
+      var url = '/_plugins'
+      $.ajax({
+        url: url,
+        type: 'POST',
+        data: plugin_spec,
+        contentType: 'application/json', // what we send to the server
+        dataType: 'json', // expected from the server
+        processData: false, // keep our precious JSON
+        success: function(data, textStatus, jqXhr) {
+          if(textStatus == "success") {
+            var action = delete_plugin ? 'Uninstalled' : 'Installed';
+            button.html('Sucessfully ' + action);
+            button.attr('disabled', true);
+          } else {
+            button.html(textStatus);
+          }
+        },
+        beforeSend: function(xhr) {
+          xhr.setRequestHeader('Accept', 'application/json');
+        },
+      });
+    });
+  </script>
+  <style type="text/css">
+  .row {
+    background-color: #FFF;
+    padding:1em;
+    margin-bottom:1em;
+  }
+  </style>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/replication/assets/less/replication.less
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/replication/assets/less/replication.less b/share/www/fauxton/src/app/addons/replication/assets/less/replication.less
new file mode 100644
index 0000000..a301966
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/replication/assets/less/replication.less
@@ -0,0 +1,196 @@
+// 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.
+
+
+@brown: #3A2C2B;
+@red: #E33F3B;
+@darkRed: #AF2D24;
+@linkRed: #DA4F49;
+@greyBrown: #A59D9D;
+@fontGrey: #808080;
+@secondarySidebar: #E4DFDC;
+
+
+form#replication {
+	position: relative;
+	max-width: none;
+	width: auto;
+
+	.form_set{
+		width: 350px;
+		display: inline-block;
+		border: 1px solid @greyBrown;
+		padding: 15px 10px 0;
+		margin-bottom: 20px;
+		&.middle{
+			width: 100px;
+			border: none;
+			position: relative;
+			height: 100px;
+			margin: 0;
+		}
+		input, select {
+			margin: 0 0 16px 5px;
+			height: 40px;
+			width: 318px;
+		}
+		.btn-group{
+			margin: 0 0 16px 5px;
+			.btn {
+				padding: 10px 57px;
+			}
+		}
+		&.local{
+			.local_option{
+				display: block;
+			}
+			.remote_option{
+				display: none;
+			}
+			.local-btn{
+				background-color: @red;
+				color: #fff;
+			}
+			.remote-btn{
+				background-color: #f5f5f5;
+				color: @fontGrey;
+			}
+		}
+		.local_option{
+			display: none;
+		}
+		.remote-btn{
+			background-color: @red;
+			color: #fff;
+		}
+	}
+
+
+	.options {
+		position: relative;
+		&:after{
+			content: '';
+			display: block;
+			position: absolute;
+			right: -16px;
+			top: 9px;
+			width: 0; 
+			height: 0; 
+			border-left: 5px solid transparent;
+			border-right: 5px solid transparent;
+			border-bottom: 5px solid black;
+			border-top: none;
+		}
+		&.off {
+			&:after{
+			content: '';
+			display: block;
+			position: absolute;
+			right: -16px;
+			top: 9px;
+			width: 0; 
+			height: 0; 
+			border-left: 5px solid transparent;
+			border-right: 5px solid transparent;
+			border-bottom: none;
+			border-top: 5px solid black;
+			}
+		}
+	}
+	.control-group{
+		label{
+			float: left;
+			min-height: 30px;
+			vertical-align: top;
+			padding-right: 5px;
+			min-width: 130px;
+			padding-left: 0px;
+		}
+		input[type=radio],
+		input[type=checkbox]{
+			margin: 0 0 2px 0;
+		}
+	}
+
+	.circle{
+		z-index: 0;
+		position: absolute;
+		top: 20px;
+		left: 15px;
+
+		&:after {
+			width: 70px;
+			height: 70px;
+			content: '';
+			display: block;
+			position: relative;
+			margin: 0 auto;
+			border: 1px solid @greyBrown;
+			-webkit-border-radius: 40px;
+			-moz-border-radius: 40px;
+			border-radius:40px;
+		}
+	}
+	.swap {
+		text-decoration: none;
+		z-index: 30;
+		cursor: pointer;
+		position: absolute;
+		font-size: 40px;
+		width: 27px;
+		height: 12px;
+		top: 31px;
+		left: 30px;
+		&:hover {
+			color: @greyBrown;
+		}
+	}
+
+}
+#replicationStatus{
+	&.showHeader{
+		li.header{
+			display: block;
+			border: none;
+		}
+		ul {
+			border:1px solid @greyBrown;
+		}
+	}
+	li.header{
+		display: none;
+	}
+	ul{
+		margin: 0;
+		li{
+			.progress,
+			p{
+				margin: 0px;
+				vertical-align: bottom;
+				&.break {
+					-ms-word-break: break-all;
+					word-break: break-all;
+
+					/* Non standard for webkit */
+					word-break: break-word;
+					-webkit-hyphens: auto;
+					-moz-hyphens: auto;
+					hyphens: auto;
+				}
+			}
+			padding: 10px 10px;
+			margin: 0;
+			list-style: none;
+			border-top: 1px solid @greyBrown;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/replication/base.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/replication/base.js b/share/www/fauxton/src/app/addons/replication/base.js
new file mode 100644
index 0000000..93965c1
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/replication/base.js
@@ -0,0 +1,24 @@
+// 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.
+
+define([
+  "app",
+  "api",
+  "addons/replication/route"
+],
+
+function(app, FauxtonAPI, replication) {
+	replication.initialize = function() {
+    FauxtonAPI.addHeaderLink({title: "Replication", href: "#/replication", icon: "fonticon-replicate",});
+  };
+  return replication;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/replication/resources.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/replication/resources.js b/share/www/fauxton/src/app/addons/replication/resources.js
new file mode 100644
index 0000000..14f255a
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/replication/resources.js
@@ -0,0 +1,69 @@
+// 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.
+
+define([
+  "app",
+  "api",
+  'addons/activetasks/resources'
+],
+
+function (app, FauxtonAPI, ActiveTasks) {
+  var Replication = {};
+
+  //these are probably dupes from the database modules. I'm going to keep them seperate for now.
+  Replication.DBModel = Backbone.Model.extend({
+    label: function () {
+      //for autocomplete
+        return this.get("name");
+    }
+  });
+
+  Replication.DBList = Backbone.Collection.extend({
+    model: Replication.DBModel,
+    url: function() {
+      return app.host + "/_all_dbs";
+    },
+    parse: function(resp) {
+      // TODO: pagination!
+      return _.map(resp, function(database) {
+        return {
+          id: database,
+          name: database
+        };
+      });
+    }
+  });
+
+  Replication.Task = Backbone.Model.extend({});
+
+  Replication.Tasks = Backbone.Collection.extend({
+    model: Replication.Task,
+    url: function () {
+      return app.host + '/_active_tasks';
+    },
+    parse: function(resp){
+      //only want replication tasks to return
+      return _.filter(resp, function(task){
+        return task.type === "replication";
+      });
+    }
+  });
+
+  Replication.Replicate = Backbone.Model.extend({
+    documentation: "replication_doc",
+    url: function(){
+      return app.host + "/_replicate";
+    }
+  });
+
+  return Replication;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/replication/route.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/replication/route.js b/share/www/fauxton/src/app/addons/replication/route.js
new file mode 100644
index 0000000..17368f8
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/replication/route.js
@@ -0,0 +1,50 @@
+// 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.
+
+define([
+  "app",
+  "api",
+  "addons/replication/resources",
+  "addons/replication/views"
+],
+function(app, FauxtonAPI, Replication, Views) {
+  var  RepRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "one_pane",
+    roles: ["_admin"],
+    routes: {
+      "replication": "defaultView",
+      "replication/:dbname": "defaultView"
+    },
+    selectedHeader: "Replication",
+    apiUrl: function() {
+      return [this.replication.url(), this.replication.documentation];
+    },
+    crumbs: [
+      {"name": "Replicate changes from: ", "link": "replication"}
+    ],
+    defaultView: function(dbname){
+			this.databases = new Replication.DBList({});
+      this.tasks = new Replication.Tasks({id: "ReplicationTasks"});
+      this.replication = new Replication.Replicate({});
+			this.setView("#dashboard-content", new Views.ReplicationForm({
+        selectedDB: dbname ||"",
+				collection: this.databases,
+        status:  this.tasks
+			}));  
+    }
+  });
+
+
+	Replication.RouteObjects = [RepRouteObject];
+
+  return Replication;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/replication/templates/form.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/replication/templates/form.html b/share/www/fauxton/src/app/addons/replication/templates/form.html
new file mode 100644
index 0000000..32a87dc
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/replication/templates/form.html
@@ -0,0 +1,74 @@
+<!--
+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.
+-->
+
+<form id="replication" class="form-horizontal">
+		<div class="from form_set  local">
+			<div class="btn-group">
+			  <button class="btn local-btn" type="button" value="local">Local</button>
+			  <button class="btn remote-btn" type="button" value="remote">Remote</button>
+			</div>
+
+			<div class="from_local local_option">
+				<select id="from_name" name="source">
+					<% _.each( databases, function( db, i ){ %>
+					   <option value="<%=db.name%>" <% if(selectedDB == db.name){%>selected<%}%> ><%=db.name%></option>
+					<% }); %>
+				</select>
+			</div>
+			<div class="from_to_remote remote_option">
+				<input type="text" id="from_url" name="source" size="30" value="http://">
+			</div>
+		</div>
+
+		<div class="form_set middle">
+			<span class="circle "></span>
+				<a href="#" title="Switch Target and Source" class="swap">
+					<span class="fonticon-swap-arrows"></span>
+				</a>
+			</span>
+		</div>
+
+		<div class="to form_set local">
+			<div class="btn-group">
+			  <button class="btn local-btn" type="button" value="local">Local</button>
+			  <button class="btn remote-btn" type="button" value="remote">Remote</button>
+			</div>
+			<div class="to_local local_option">
+				<input type="text" id="to_name" name="target" size="30" placeholder="database name">
+			</div>
+
+			<div class="to_remote remote_option">
+				<input type="text" id="to_url" name="target" size="30" value="http://">
+			</div>
+		</div>
+
+
+	<div class="actions">
+		<div class="control-group">
+			<label for="continuous">
+				<input type="checkbox" name="continuous" value="true" id="continuous">
+				Continuous
+			</label>
+
+			<label for="createTarget">
+				<input type="checkbox" name="create_target" value="true" id="createTarget">
+				Create Target <a href="<%=getDocUrl('replication_doc')%>" target="_blank"><i class="icon-question-sign" rel="tooltip" title="Create the target database"></i></a>
+			</label>
+		</div>
+
+		<button class="btn btn-success btn-large save" type="submit">Replicate</button>
+	</div>
+</form>
+
+<div id="replicationStatus"></div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/replication/templates/progress.html
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/replication/templates/progress.html b/share/www/fauxton/src/app/addons/replication/templates/progress.html
new file mode 100644
index 0000000..1e6ef90
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/replication/templates/progress.html
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<p class="span6 break">Replicating <strong><%=source%></strong> to <strong><%=target%></strong></p>
+
+<div class="span4 progress progress-striped active">
+  <div class="bar" style="width: <%=progress || 0%>%;"><%=progress || "0"%>%</div>
+</div>
+
+<span class="span1">
+	<button class="cancel btn btn-danger btn-large delete" data-source="<%=source%>"  data-rep-id="<%=repid%>" data-continuous="<%=continuous%>" data-target="<%=target%>">Cancel</a>
+</span>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/replication/tests/replicationSpec.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/replication/tests/replicationSpec.js b/share/www/fauxton/src/app/addons/replication/tests/replicationSpec.js
new file mode 100644
index 0000000..788b082
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/replication/tests/replicationSpec.js
@@ -0,0 +1,28 @@
+// 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.
+define([
+       'addons/replication/base',
+       'chai'
+], function (Replication, chai) {
+  var expect = chai.expect;
+
+  describe('Replication Addon', function(){
+
+    describe('Replication DBList Collection', function () {
+      var rep;
+
+      beforeEach(function () {
+        rep = new rep.DBList(["foo","bar","baz","bo"]);
+      });
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/replication/views.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/replication/views.js b/share/www/fauxton/src/app/addons/replication/views.js
new file mode 100644
index 0000000..f4b96fd
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/replication/views.js
@@ -0,0 +1,295 @@
+// 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.
+
+define([
+       "app",
+       "api",
+       "modules/fauxton/components",
+       "addons/replication/resources"
+],
+function(app, FauxtonAPI, Components, replication) {
+  var View = {},
+  Events ={},
+  pollingInfo ={
+    rate: 5,
+    intervalId: null
+  };
+
+  _.extend(Events, Backbone.Events);
+
+  // NOTES: http://wiki.apache.org/couchdb/Replication
+
+  // Replication form view is huge
+  // -----------------------------------
+  // afterRender: autocomplete on the target input field
+  // beforeRender:  add the status table
+  // disableFields:  disable non active fields on submit 
+  // enableFields:  enable field when radio btns are clicked
+  // establish:  get the DB list for autocomplete
+  // formValidation:  make sure fields aren't empty
+  // showProgress:  make a call to active_tasks model and show only replication types.  Poll every 5 seconds. (make this it's own view)
+  // startReplication:  saves to the model, starts replication
+  // submit:  form submit handler
+  // swapFields:  change to and from target
+  // toggleAdvancedOptions:  toggle advanced
+
+  View.ReplicationForm = FauxtonAPI.View.extend({
+    template: "addons/replication/templates/form",
+    events:  {
+      "submit #replication": "validate",
+      "click .btn-group .btn": "showFields",
+      "click .swap": "swapFields",
+      "click .options": "toggleAdvancedOptions"
+    },
+    initialize: function(options){
+      this.status = options.status;
+      this.selectedDB = options.selectedDB;
+      this.newRepModel = new replication.Replicate({});
+    },
+    afterRender: function(){
+      this.dbSearchTypeahead = new Components.DbSearchTypeahead({
+        dbLimit: 30,
+        el: "input#to_name"
+      });
+
+      this.dbSearchTypeahead.render();
+
+    },
+
+    beforeRender:  function(){
+      this.insertView("#replicationStatus", new View.ReplicationList({
+        collection: this.status
+      }));
+    },
+    cleanup: function(){
+      clearInterval(pollingInfo.intervalId);
+    },
+    enableFields: function(){
+      this.$el.find('input','select').attr('disabled',false);
+    },
+    disableFields: function(){
+      this.$el.find('input:hidden','select:hidden').attr('disabled',true);
+    },
+    showFields: function(e){
+      var $currentTarget = this.$(e.currentTarget),
+      targetVal = $currentTarget.val();
+
+      if (targetVal === "local"){
+        $currentTarget.parents('.form_set').addClass('local');
+      }else{
+        $currentTarget.parents('.form_set').removeClass('local');
+      }
+    },
+    establish: function(){
+      return [ this.collection.fetch(), this.status.fetch()];
+    },
+    validate: function(e){
+      e.preventDefault();
+      var notification;
+      if (this.formValidation()){
+        notification = FauxtonAPI.addNotification({
+          msg: "Please enter every field.",
+          type: "error",
+          clear: true
+        });
+      }else if (this.$('input#to_name').is(':visible') && !this.$('input[name=create_target]').is(':checked')){
+        var alreadyExists = this.collection.where({"name":this.$('input#to_name').val()});
+        if (alreadyExists.length === 0){
+          notification = FauxtonAPI.addNotification({
+            msg: "This database doesn't exist. Check create target if you want to create it.",
+            type: "error",
+            clear: true
+          });
+        }
+      }else{
+        this.submit(e);
+      }
+    },
+    formValidation: function(e){
+      var $remote = this.$el.find('input:visible'),
+      error = false;
+      for(var i=0; i<$remote.length; i++){
+        if ($remote[i].value =="http://" || $remote[i].value ===""){
+          error = true;
+        }
+      }
+      return error;
+    },
+    serialize: function(){
+      return {
+        databases:  this.collection.toJSON(),
+        selectedDB: this.selectedDB
+      };
+    },
+    startReplication: function(json){
+      var that = this;
+      this.newRepModel.save(json,{
+        success: function(resp){
+          var notification = FauxtonAPI.addNotification({
+            msg: "Replication from "+resp.get('source')+" to "+ resp.get('target')+" has begun.",
+            type: "success",
+            clear: true
+          });
+          that.updateButtonText(false);
+          Events.trigger('update:tasks');
+        },
+        error: function(model, xhr, options){
+          var errorMessage = JSON.parse(xhr.responseText);
+          var notification = FauxtonAPI.addNotification({
+            msg: errorMessage.reason,
+            type: "error",
+            clear: true
+          });
+          that.updateButtonText(false);
+        }
+      });
+      this.enableFields();
+    },		
+    updateButtonText: function(wait){
+      var $button = this.$('#replication button[type=submit]');
+      if(wait){
+        $button.text('Starting replication...').attr('disabled', true);
+      } else {
+        $button.text('Replication').attr('disabled', false);
+      }
+    },
+    submit: function(e){
+      this.disableFields(); 
+      var formJSON = {};
+      _.map(this.$(e.currentTarget).serializeArray(), function(formData){
+        if(formData.value !== ''){
+          formJSON[formData.name] = (formData.value ==="true"? true: formData.value.replace(/\s/g, '').toLowerCase());
+        }
+      });
+
+      this.updateButtonText(true);
+      this.startReplication(formJSON);
+    },	
+    swapFields: function(e){
+      e.preventDefault();
+      //WALL O' VARIABLES
+      var $fromSelect = this.$('#from_name'),
+          $toSelect = this.$('#to_name'),
+          $toInput = this.$('#to_url'),
+          $fromInput = this.$('#from_url'),
+          fromSelectVal = $fromSelect.val(),
+          fromInputVal = $fromInput.val(),
+          toSelectVal = $toSelect.val(),
+          toInputVal = $toInput.val();
+
+      $fromSelect.val(toSelectVal);
+      $toSelect.val(fromSelectVal);
+
+      $fromInput.val(toInputVal);
+      $toInput.val(fromInputVal);
+    }
+  });
+
+
+  View.ReplicationList = FauxtonAPI.View.extend({
+    tagName: "ul",
+    initialize:  function(){
+      Events.bind('update:tasks', this.establish, this);
+      this.listenTo(this.collection, "reset", this.render);
+      this.$el.prepend("<li class='header'><h4>Active Replication Tasks</h4></li>");
+    },
+    establish: function(){
+      return [this.collection.fetch({reset: true})];
+    },
+    setPolling: function(){
+      var that = this;
+      this.cleanup();
+      pollingInfo.intervalId = setInterval(function() {
+        that.establish();
+      }, pollingInfo.rate*1000);
+    },
+    cleanup: function(){
+      clearInterval(pollingInfo.intervalId);
+    },
+    beforeRender:  function(){
+      this.collection.forEach(function(item) {
+        this.insertView(new View.replicationItem({ 
+          model: item
+        }));
+      }, this);
+    },
+    showHeader: function(){
+      if (this.collection.length > 0){
+        this.$el.parent().addClass('showHeader');
+      } else {
+        this.$el.parent().removeClass('showHeader');
+      }
+    },
+    afterRender: function(){
+      this.showHeader();
+      this.setPolling();
+    }
+  });
+
+  //make this a table row item.
+  View.replicationItem = FauxtonAPI.View.extend({
+    tagName: "li",
+    className: "row",
+    template: "addons/replication/templates/progress",
+    events: {
+      "click .cancel": "cancelReplication"
+    },
+    initialize: function(){
+      this.newRepModel = new replication.Replicate({});
+    },
+    establish: function(){
+      return [this.model.fetch()];
+    },
+    cancelReplication: function(e){
+      //need to pass "cancel": true with source & target
+      var $currentTarget = this.$(e.currentTarget),
+      repID = $currentTarget.attr('data-rep-id');
+      this.newRepModel.save({
+        "replication_id": repID,
+        "cancel": true
+      },
+      {
+        success: function(model, xhr, options){
+          var notification = FauxtonAPI.addNotification({
+            msg: "Replication stopped.",
+            type: "success",
+            clear: true
+          });
+        },
+        error: function(model, xhr, options){
+          var errorMessage = JSON.parse(xhr.responseText);
+          var notification = FauxtonAPI.addNotification({
+            msg: errorMessage.reason,
+            type: "error",
+            clear: true
+          });
+        }
+      });
+    },
+    afterRender: function(){
+      if (this.model.get('continuous')){
+        this.$el.addClass('continuous');
+      }
+    },
+    serialize: function(){
+      return {
+        progress:  this.model.get('progress'),
+        target: this.model.get('target'),
+        source: this.model.get('source'),
+        continuous: this.model.get('continuous'),
+        repid: this.model.get('replication_id')
+      };
+    }
+  });
+
+  return View;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/stats/assets/less/stats.less
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/stats/assets/less/stats.less b/share/www/fauxton/src/app/addons/stats/assets/less/stats.less
new file mode 100644
index 0000000..cfa7679
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/stats/assets/less/stats.less
@@ -0,0 +1,19 @@
+// 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.
+
+.datatypes {
+  border: #d3d3d3 1px solid;
+  -webkit-border-radius: 5px;
+  -moz-border-radius: 5px;
+  border-radius: 5px;
+  padding: 15px;
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/stats/base.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/stats/base.js b/share/www/fauxton/src/app/addons/stats/base.js
new file mode 100644
index 0000000..1b44b8b
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/stats/base.js
@@ -0,0 +1,26 @@
+// 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.
+
+define([
+  "app",
+  "api",
+  "addons/stats/routes"
+],
+
+function(app, FauxtonAPI, Stats) {
+
+  Stats.initialize = function() {
+    FauxtonAPI.addHeaderLink({title: "Statistics", href: "#stats", icon: "fonticon-stats", className: 'stats'});
+  };
+
+  return Stats;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/stats/resources.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/stats/resources.js b/share/www/fauxton/src/app/addons/stats/resources.js
new file mode 100644
index 0000000..a761e6b
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/stats/resources.js
@@ -0,0 +1,38 @@
+// 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.
+
+define([
+       "app",
+       "api",
+       "backbone",
+       "lodash",
+       "modules/fauxton/base"
+],
+
+function (app, FauxtonAPI, backbone, _, Fauxton) {
+  var Stats = new FauxtonAPI.addon();
+
+  Stats.Collection = Backbone.Collection.extend({
+    model: Backbone.Model,
+    documentation: "stats",
+    url: app.host+"/_stats",
+    parse: function(resp) {
+      return _.flatten(_.map(resp, function(doc, key) {
+        return _.map(doc, function(v, k){
+          return _.extend({id: k, type: key}, v);
+        });
+      }), true);
+    }
+  });
+
+  return Stats;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c14b2991/share/www/fauxton/src/app/addons/stats/routes.js
----------------------------------------------------------------------
diff --git a/share/www/fauxton/src/app/addons/stats/routes.js b/share/www/fauxton/src/app/addons/stats/routes.js
new file mode 100644
index 0000000..971c111
--- /dev/null
+++ b/share/www/fauxton/src/app/addons/stats/routes.js
@@ -0,0 +1,63 @@
+// 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.
+
+define([
+       "app",
+       "api",
+       "addons/stats/views"
+],
+
+function(app, FauxtonAPI, Stats) {
+
+  var StatsRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "with_sidebar",
+
+    routes: {
+      "stats":"showStats",
+      "_stats": "showStats"
+    },
+    
+    
+    crumbs: [
+      {"name": "Statistics", "link": "_stats"}
+    ],
+
+    selectedHeader: "Statistics",
+
+    initialize: function () {
+      this.stats = new Stats.Collection();
+
+      this.setView("#sidebar-content", new Views.StatSelect({
+        collection: this.stats
+      }));
+
+    },
+
+    showStats: function () {
+      this.setView("#dashboard-content", new Views.Statistics({
+        collection: this.stats
+      }));
+    },
+
+    establish: function() {
+      return [this.stats.fetch()];
+    },
+
+    apiUrl: function(){
+      return [ this.stats.url, this.stats.documentation]; 
+    }
+  });
+
+  Stats.RouteObjects = [StatsRouteObject];
+
+  return Stats;
+});