You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2013/09/10 16:03:54 UTC

[08/14] git commit: updated refs/heads/index-pagination to db85b98

Replication addon sans tests
Fixes from code review
Should use _.filter, not _.map when pulling out all the replication active tasks.
fixing CSS overrides on the form
Fixed Create target. Change swap to icon,  added tooltips, made inputs wider.
Fixed continuous, scrubbed whitepaces from db names, updated layout


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/e0e38583
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/e0e38583
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/e0e38583

Branch: refs/heads/index-pagination
Commit: e0e385838e562111c0c9b41cfc2649b1a52e92f8
Parents: 466a7ec
Author: suelockwood <de...@gmail.com>
Authored: Mon Sep 9 15:38:56 2013 -0400
Committer: suelockwood <de...@gmail.com>
Committed: Mon Sep 9 16:14:54 2013 -0400

----------------------------------------------------------------------
 .gitignore                                      |   1 +
 .../replication/assets/less/replication.less    | 175 +++++++++++
 src/fauxton/app/addons/replication/base.js      |  24 ++
 src/fauxton/app/addons/replication/resources.js |  68 ++++
 src/fauxton/app/addons/replication/route.js     |  47 +++
 .../app/addons/replication/templates/form.html  |  74 +++++
 .../addons/replication/templates/progress.html  |  22 ++
 .../addons/replication/tests/replicationSpec.js |  28 ++
 src/fauxton/app/addons/replication/views.js     | 311 +++++++++++++++++++
 src/fauxton/assets/less/fauxton.less            |   4 +-
 src/fauxton/settings.json.default               |   1 +
 11 files changed, 753 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/e0e38583/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index a022b14..a4798c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,6 +91,7 @@ src/fauxton/app/addons/*
 !src/fauxton/app/addons/config
 !src/fauxton/app/addons/logs
 !src/fauxton/app/addons/stats
+!src/fauxton/app/addons/replication
 !src/fauxton/app/addons/contribute
 !src/fauxton/app/addons/auth
 !src/fauxton/app/addons/exampleAuth

http://git-wip-us.apache.org/repos/asf/couchdb/blob/e0e38583/src/fauxton/app/addons/replication/assets/less/replication.less
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/assets/less/replication.less b/src/fauxton/app/addons/replication/assets/less/replication.less
new file mode 100644
index 0000000..ec3b718
--- /dev/null
+++ b/src/fauxton/app/addons/replication/assets/less/replication.less
@@ -0,0 +1,175 @@
+/**/
+
+@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;
+			}
+			padding: 10px 10px;
+			margin: 0;
+			list-style: none;
+			border-top: 1px solid @greyBrown;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/e0e38583/src/fauxton/app/addons/replication/base.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/base.js b/src/fauxton/app/addons/replication/base.js
new file mode 100644
index 0000000..93965c1
--- /dev/null
+++ b/src/fauxton/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/e0e38583/src/fauxton/app/addons/replication/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/resources.js b/src/fauxton/app/addons/replication/resources.js
new file mode 100644
index 0000000..38ae139
--- /dev/null
+++ b/src/fauxton/app/addons/replication/resources.js
@@ -0,0 +1,68 @@
+// 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({
+    url: function(){
+      return app.host + "/_replicate";
+    }
+  });
+
+  return Replication;
+});

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

http://git-wip-us.apache.org/repos/asf/couchdb/blob/e0e38583/src/fauxton/app/addons/replication/templates/form.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/templates/form.html b/src/fauxton/app/addons/replication/templates/form.html
new file mode 100644
index 0000000..eeb3081
--- /dev/null
+++ b/src/fauxton/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%>"><%=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 <i class="icon-question-sign " rel="tooltip" title="Create the target database"></i>
+			</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/e0e38583/src/fauxton/app/addons/replication/templates/progress.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/templates/progress.html b/src/fauxton/app/addons/replication/templates/progress.html
new file mode 100644
index 0000000..02c47f2
--- /dev/null
+++ b/src/fauxton/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">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/e0e38583/src/fauxton/app/addons/replication/tests/replicationSpec.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/tests/replicationSpec.js b/src/fauxton/app/addons/replication/tests/replicationSpec.js
new file mode 100644
index 0000000..788b082
--- /dev/null
+++ b/src/fauxton/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/e0e38583/src/fauxton/app/addons/replication/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/views.js b/src/fauxton/app/addons/replication/views.js
new file mode 100644
index 0000000..4b55030
--- /dev/null
+++ b/src/fauxton/app/addons/replication/views.js
@@ -0,0 +1,311 @@
+// 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"
+],
+function(app, FauxtonAPI, 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.newRepModel = new replication.Replicate({});
+		},
+		afterRender: function(){
+			var dbLimit = 30;
+      var ajaxReq;
+      //re-using what we have on DB search
+      this.$el.find("input#to_name").typeahead({
+        source: function(query, process) {
+          var url = [
+            app.host,
+            "/_all_dbs?startkey=%22",
+            query,
+            "%22&endkey=%22",
+            query,
+            "\u9999%22&limit=",
+            dbLimit
+          ].join('');
+          if (ajaxReq) ajaxReq.abort();
+          ajaxReq = $.ajax({
+            url: url,
+            dataType: 'json',
+            success: function(data) {
+              process(data);
+            }
+          });
+        }
+      });
+
+		},
+
+		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()
+			};
+		},
+		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/e0e38583/src/fauxton/assets/less/fauxton.less
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/less/fauxton.less b/src/fauxton/assets/less/fauxton.less
index 185113e..1e0ccd2 100644
--- a/src/fauxton/assets/less/fauxton.less
+++ b/src/fauxton/assets/less/fauxton.less
@@ -166,9 +166,9 @@ a:hover{
   top: 0px;
   display: block;
   z-index: 100000;
-  left: 340px;
+  left: @navWidth;
   .closeMenu & {
-    left: 63px;
+    left: @collapsedNavWidth;
   }
   width: 100%;
 }

http://git-wip-us.apache.org/repos/asf/couchdb/blob/e0e38583/src/fauxton/settings.json.default
----------------------------------------------------------------------
diff --git a/src/fauxton/settings.json.default b/src/fauxton/settings.json.default
index 2b7fe89..81cb4cb 100644
--- a/src/fauxton/settings.json.default
+++ b/src/fauxton/settings.json.default
@@ -4,6 +4,7 @@
   { "name": "config" },
   { "name": "logs" },
   { "name": "stats" },
+  { "name":  "replication" },
   { "name": "contribute" },
   { "name": "auth" }
   ],