You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by de...@apache.org on 2013/09/09 15:19:17 UTC

[07/10] git commit: updated refs/heads/1807-Replication to 38970f1

Replication addon sans tests


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

Branch: refs/heads/1807-Replication
Commit: a2a56978b7e23c9921231a4ce4a1fa4baf7679b9
Parents: 0bc81f9
Author: suelockwood <de...@gmail.com>
Authored: Thu Aug 22 15:26:20 2013 -0400
Committer: suelockwood <de...@gmail.com>
Committed: Fri Sep 6 14:20:10 2013 -0400

----------------------------------------------------------------------
 .gitignore                                      |   1 +
 .../replication/assets/less/replication.less    | 129 +++++++++
 src/fauxton/app/addons/replication/base.js      |  24 ++
 src/fauxton/app/addons/replication/resources.js |  70 +++++
 src/fauxton/app/addons/replication/route.js     |  47 +++
 .../app/addons/replication/templates/form.html  |  76 +++++
 .../addons/replication/templates/progress.html  |  22 ++
 .../addons/replication/tests/replicationSpec.js |  28 ++
 src/fauxton/app/addons/replication/views.js     | 290 +++++++++++++++++++
 src/fauxton/settings.json.default               |   1 +
 10 files changed, 688 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/a2a56978/.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/a2a56978/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..bcfdb56
--- /dev/null
+++ b/src/fauxton/app/addons/replication/assets/less/replication.less
@@ -0,0 +1,129 @@
+#replication {
+	position: relative;
+
+	.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;
+		}
+		input[type=text],
+		input[type=radio],
+		input[type=checkbox],
+		select {
+			margin-top: -2px;
+		}
+	}
+
+
+	.swap {
+			cursor: pointer;
+			position: absolute;
+			font-size: 12px;
+			width: 27px;
+			height: 12px;
+			display: block;
+			border-top: 3px solid #8E9292;
+			border-bottom: 3px solid #8E9292;
+			top: 20%;
+			left: 42%;
+			&:hover {
+				border-color: #2C2C2C;
+				&:before {
+					bottom: -8px;
+					right: 23px;
+					position: absolute;
+					border-right: 7px solid #2C2C2C;
+					border-top: 7px solid rgba(44, 44, 44, 0);
+					border-bottom: 7px solid rgba(44, 44, 44, 0);
+					content: "";
+				}
+				&:after {
+					top: -8px;
+					left: 23px;
+					position: absolute;
+					border-left: 7px solid #2C2C2C;
+					border-top: 7px solid rgba(44, 44, 44, 0);
+					border-bottom: 7px solid rgba(44, 44, 44, 0);
+					content: "";
+				}
+		}
+		&:before {
+			bottom: -8px;
+			right: 23px;
+			position: absolute;
+			border-right: 7px solid #8E9292;
+			border-top: 7px solid rgba(44, 44, 44, 0);
+			border-bottom: 7px solid rgba(44, 44, 44, 0);
+			content: "";
+
+		}
+		&:after {
+			top: -8px;
+			left: 23px;
+			position: absolute;
+			border-left: 7px solid #8E9292;
+			border-top: 7px solid rgba(44, 44, 44, 0);
+			border-bottom: 7px solid rgba(44, 44, 44, 0);
+			content: "";
+		}
+	}
+
+}
+#replicationStatus{
+	&.showHeader{
+		li.header{
+			display: block;
+		}
+	}
+	li.header{
+		display: none;
+	}
+	ul{
+		margin: 0;
+		li{
+			.progress,
+			p{
+				margin: 0px;
+				vertical-align: bottom;
+			}
+			padding: 10px 0 ;
+			margin: 0;
+			list-style: none;
+			border-bottom: 1px solid #e3e3e3;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/a2a56978/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/a2a56978/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..67b474a
--- /dev/null
+++ b/src/fauxton/app/addons/replication/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",
+  '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 _.map(resp, function(task){
+        if (task.type === "replication"){
+          return task;
+        }
+      });
+    }
+  });
+
+  Replication.Replicate = Backbone.Model.extend({
+    url: function(){
+      return app.host + "/_replicate";
+    }
+  });
+
+  return Replication;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/a2a56978/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..4002203
--- /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"}); //replace with with a call to active tasks when that is merged in
+			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/a2a56978/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..c1711b5
--- /dev/null
+++ b/src/fauxton/app/addons/replication/templates/form.html
@@ -0,0 +1,76 @@
+<!--
+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="row">
+		<div class="span5 from form_set">
+			<div class="control-group from_local">
+				<label for="from_local">
+					<input type="radio" id="from_local" name="from_type" value="local" checked="" class="setType"> 
+					Local database: 
+				</label>
+				<select id="from_name" name="source">
+					<% _.each( databases, function( db, i ){ %>
+					   <option value="<%=db.name%>"><%=db.name%></option>
+					<% }); %>
+				</select>
+			</div>
+
+			<div class="control-group from_to_remote">
+				<label for="from_to_remote">
+					<input type="radio" id="from_to_remote" name="from_type" value="remote" class="setType"> 
+					Remote database:
+				</label>
+				<input type="text" id="from_url" name="source" size="30" value="http://" disabled="true">
+			</div>
+		</div>
+
+		
+		<div class="span2">
+			<span class="swap">
+			</span>
+		</div>
+
+		<div class="to form_set span5">
+			<div class="to_local control-group">
+				<label for="to_local">
+					<input type="radio" id="to_local" name="to_type" value="local" checked="" class="setType"> 
+					Local database: 
+				</label>
+				<input type="text" id="to_name" name="target" size="30">
+			</div>
+
+			<div class="to_remote control-group">
+				<label for="to_remote">
+					<input type="radio" id="to_remote" name="to_type" value="remote" class="setType"> 
+					Remote database: 
+				</label>
+				<input type="text" id="to_url" name="target" size="30" value="http://" disabled="true">
+			</div>
+		</div>
+	</div>
+
+	<div class="actions">
+		<div class="control-group">
+			<label for="continuous">
+				<input type="checkbox" name="continuous" value="continuous" id="continuous">
+				Continuous
+			</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/a2a56978/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..d93db68
--- /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 <%=source%> to <%=target%>. Please wait... :)</p>
+
+<div class="span5 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/a2a56978/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/a2a56978/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..30c642f
--- /dev/null
+++ b/src/fauxton/app/addons/replication/views.js
@@ -0,0 +1,290 @@
+// 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": "submit",
+			"click input[type=radio]": "enableFields",
+			"click .swap": "swapFields",
+			"click .options": "toggleAdvancedOptions"
+		},
+		initialize: function(){
+			this.status = this.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);
+		},
+		disableFields: function(){
+			this.$('input[type=radio]').attr('disabled',true);
+			this.$('.advancedOptions:hidden').find('input').attr('disabled',true);
+		},
+
+		enableFields: function(e){
+			var $currentTarget = this.$(e.currentTarget);
+					$currentTarget.parents(".form_set").find('input[type="text"], select').attr('disabled','true').addClass('disabled');
+					$currentTarget.parents('.control-group').find('input[type="text"], select').removeAttr('disabled').removeClass('disabled');
+		},
+		establish: function(){
+			return [ this.collection.fetch(), this.status.fetch()];
+		},
+
+		formValidation: function(){
+			var $remote = this.$el.find("[value='remote']:checked").parents('.control-group').find('input[type=text]'),
+					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);
+				}
+			});
+		},		
+		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){
+			e.preventDefault();
+			this.disableFields(); //disable fields not relevant to submitting
+
+			var formJSON = {};
+			_.map(this.$(e.currentTarget).serializeArray(), function(formData){
+				if(formData.value !== ''){
+					formJSON[formData.name] = formData.value;
+				}
+			});
+
+			var alreadyExists = this.collection.where({"name":$('input#to_name').val()});
+
+			if (alreadyExists.length === 0){
+				formJSON.create_target = true;
+			}
+			console.log($(e.currentTarget).serializeArray(), formJSON);
+			this.updateButtonText(true);
+			this.startReplication(formJSON);
+		},	
+		swapFields: function(e){
+			//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'><h2>Active Replication Tasks</h2></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(){
+		var that = this;
+    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 = $(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/a2a56978/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" }
   ],