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 2014/03/26 02:00:26 UTC

couchdb commit: updated refs/heads/Fauxton-replicator-addon to 85c2872

Repository: couchdb
Updated Branches:
  refs/heads/Fauxton-replicator-addon [created] 85c28725f


Replicator & ReplicatorAuth extension


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

Branch: refs/heads/Fauxton-replicator-addon
Commit: 85c28725fbb9e0c2446984dc23eaeb4121d3b860
Parents: 1fb7cea
Author: suelockwood <de...@apache.org>
Authored: Wed Jan 15 19:31:23 2014 -0500
Committer: suelockwood <de...@apache.org>
Committed: Tue Mar 25 20:59:57 2014 -0400

----------------------------------------------------------------------
 .gitignore                                      |   1 +
 .../replicator/assets/less/replicator.less      | 255 ++++++
 src/fauxton/app/addons/replicator/base.js       |  26 +
 src/fauxton/app/addons/replicator/happy.js      | 177 +++++
 src/fauxton/app/addons/replicator/resources.js  | 230 ++++++
 src/fauxton/app/addons/replicator/route.js      | 101 +++
 .../app/addons/replicator/templates/active.html |  41 +
 .../addons/replicator/templates/complete.html   |  43 +
 .../app/addons/replicator/templates/error.html  |  41 +
 .../app/addons/replicator/templates/form.html   |  71 ++
 .../replicator/templates/localremotetabs.html   |  39 +
 .../app/addons/replicator/templates/nope.html   |  17 +
 .../addons/replicator/templates/options.html    |  35 +
 .../replicator/templates/password_modal.html    |  27 +
 .../replicator/templates/sidebar_no_header.html |  20 +
 .../replicator/templates/sidebartabs.html       |  30 +
 .../replicator/templates/statustable.html       |  23 +
 .../addons/replicator/templates/validator.html  | 125 +++
 .../addons/replicator/tests/replicatorSpec.js   |  28 +
 src/fauxton/app/addons/replicator/views.js      | 776 +++++++++++++++++++
 src/fauxton/settings.json.default               |   1 +
 21 files changed, 2107 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 5e4d220..b050f07 100644
--- a/.gitignore
+++ b/.gitignore
@@ -96,6 +96,7 @@ src/fauxton/app/addons/*
 !src/fauxton/app/addons/plugins
 !src/fauxton/app/addons/logs
 !src/fauxton/app/addons/stats
+!src/fauxton/app/addons/replicator
 !src/fauxton/app/addons/replication
 !src/fauxton/app/addons/contribute
 !src/fauxton/app/addons/auth

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/assets/less/replicator.less
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/assets/less/replicator.less b/src/fauxton/app/addons/replicator/assets/less/replicator.less
new file mode 100644
index 0000000..38da2a2
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/assets/less/replicator.less
@@ -0,0 +1,255 @@
+// 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: #9d261d;
+@darkRed: #AF2D24;
+@linkRed: #DA4F49;
+@greyBrown: #A59D9D;
+@fontGrey: #808080;
+@secondarySidebar: #E4DFDC;
+@green: #46a546;
+
+#replication-status{
+	button.close{
+		font-size: 30px;
+	}
+}
+
+
+.replication-sidebar{
+	top: 0;
+}
+
+dl.replication-validator{
+	dt{
+		width: 300px;
+		text-align: left;
+		padding: 10px;
+	}
+	dd{
+		margin-left: 300px;
+		text-align: left;
+		padding: 10px;
+		pre {
+			max-width: 450px;
+		}
+		span:before {
+			padding-right: 10px;
+		}
+		.red {
+			color: @red;
+		}
+		.green{
+			color: @green;
+		}
+	}
+}
+ table.replication-table{
+ 	margin-top: 20px;
+ }
+
+form#replication {
+	padding-top: 40px;
+	position: relative;
+	max-width: none;
+	width: auto;
+
+	.actions{
+		padding: 15px 0;
+	}
+	#from_name {
+		margin-right: 20px;
+	}
+	[type="text"]{
+		width: 100%;
+	}
+	.autharea {
+		display: inline-block;
+		vertical-align: top;
+		padding-top: 10px;
+	}
+	button.fonticon-replicate{
+		padding: 15px 20px;
+	}
+	.span12{
+		margin-left: 0;
+		padding-bottom: 20px;
+	}
+	.nav-tabs{
+		max-height: 37px;
+		margin-bottom: 0;
+		border: 0;
+	}
+	.dropdown-menu {
+		width: 85%;
+	}
+	.tab-content.small-tabs{
+		margin-top: 0;
+		border: 1px solid #e3e3e3;
+		margin-bottom: 10px;
+		.tab-pane {
+			padding: 20px 20px;
+		}
+		> .active {
+			display: block;
+			background-color: #fff;
+		}
+	}
+	h3 {
+		margin-top: 0;
+		line-height: 27px
+	}
+	.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: 140px;
+			padding-left: 0px;
+			&.control-label{
+				width: 100px;
+				min-width: 100px;
+				font-size: 16px;
+			}
+		}
+		input[type=radio],
+		input[type=checkbox]{
+			margin: 0 0 2px 0;
+		}
+		.controls {
+			margin-left: 125px;
+			padding-right: 20px;
+		}
+	}
+
+	.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;
+		}
+	}
+
+}
+
+.btn-group.toggle-btns input[type=radio] {
+display: none;
+}

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/base.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/base.js b/src/fauxton/app/addons/replicator/base.js
new file mode 100644
index 0000000..1465f66
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/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/replicator/route",
+  "addons/replicator/views",
+],
+
+function(app, FauxtonAPI, replication, Views) {
+	replication.Views = Views;
+	replication.initialize = function() {
+    FauxtonAPI.addHeaderLink({title: "Replication", href: "#/replication", icon: "fonticon-replicate",});
+  };
+  return replication;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/happy.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/happy.js b/src/fauxton/app/addons/replicator/happy.js
new file mode 100644
index 0000000..9516a66
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/happy.js
@@ -0,0 +1,177 @@
+// HAPPY JS.
+// WARNING:  this has been editted to support custom error message handling. 
+// $('#awesomeForm').isHappy({
+//   fields: {
+//     // reference the field you're talking about, probably by `id`
+//     // but you could certainly do $('[name=name]') as well.
+//     '#yourName': {
+//       required: true,
+//       message: 'Might we inquire your name'
+//     },
+//     '#email': {
+//       required: true,
+//       defaultMessage: 'How are we to reach you sans email??',
+//       test:[
+          // {
+          //   test: happy.email, // this can be *any* function that returns true or false
+          //   message: "default message here"
+          // },
+          // {
+          //   test: happy.equals,
+          //   message: "this needs to equal that"
+          // }
+//      ]
+//   },
+//   errorHandling: function(error){console.log(error.message);}
+// });
+//
+// if you don't pass in a function for errorHandling, it behaves as designed. - Sue
+
+(function($){
+  function trim(el) {
+    return (''.trim) ? el.val().trim() : $.trim(el.val());
+  }
+  $.fn.isHappy = function (config) {
+    var happy = {
+      USPhone: function (val) {
+        return (/^\(?(\d{3})\)?[\- ]?\d{3}[\- ]?\d{4}$/).test(val);
+      },
+
+      // matches mm/dd/yyyy (requires leading 0's (which may be a bit silly, what do you think?)
+      date: function (val) {
+        return (/^(?:0[1-9]|1[0-2])\/(?:0[1-9]|[12][0-9]|3[01])\/(?:\d{4})/).test(val);
+      },
+      
+      email: function (val) {
+        return (/^(?:\w+\.?)*\w+@(?:\w+\.)+\w+$/).test(val);
+      },
+      
+      minLength: function (val, length) {
+        return val.length >= length;
+      },
+      
+      maxLength: function (val, length) {
+        return val.length <= length;
+      },
+      
+      equal: function (val1, val2) {
+        return (val1 == val2);
+      }
+    };
+
+    function errorMessaging(error,selector){
+      if(config.errorHandling){
+        config.errorHandling(error);
+      }else{
+        var message = error.useRequiredMessage?error.defaultMessage:error.message;
+
+        $(selector).parent().find("#"+error.id).remove();
+        var errorEl = $('<span id="'+error.id+'" class="unhappyMessage">'+message+'</span>');
+        $(selector).after(errorEl);
+      }
+    }
+
+    function handleSubmit(e) {
+      var errors = false, i, l,
+      fields = getFields();
+      for (i = 0, l = fields.length; i < l; i += 1) {
+        if (!fields[i].testValid(true)) {
+          errors = true;
+        }
+      }
+      if (errors) {
+        if (isFunction(config.unHappy)) config.unHappy();
+        return false;
+      } else if (config.testMode) {
+        if (window.console) console.warn('would have submitted');
+        return false;
+      }
+    }
+
+    function isFunction (obj) {
+      return !!(obj && obj.constructor && obj.call && obj.apply);
+    }
+
+    function getFields(){
+      var fields = [];
+      for ( var formField in config.fields) {
+        fields.push(processField(config.fields[formField], formField));
+      }
+      return fields;
+    }
+
+    function processField(opts, selector) {
+      var field = $(selector),
+          errorM = {
+            defaultMessage: opts.defaultMessage || "This field is required",
+            message: " ",
+            id: field.attr('name') + '_unhappy',
+            useRequiredMessage: false
+          };
+
+      field.testValid = function (submit) {
+        var val,
+          el = $(this),
+          gotFunc,
+          error = false,
+          temp, 
+          required = opts.required,
+          password = (field.attr('type') === 'password'),
+          arg = isFunction(opts.arg) ? opts.arg() : opts.arg,
+          tests = opts.tests || [];
+          
+        if ($(this).length <= 0){return true;}
+        // clean it or trim it
+        if (isFunction(opts.clean)) {
+          val = opts.clean(el.val());
+        } else if (!opts.trim && !password) {
+          val = trim(el);
+        } else {
+          val = el.val();
+        }
+        
+        // write it back to the field
+        el.val(val);
+        
+        // get the value
+        gotTests = ((val.length > 0 || required === 'sometimes') && tests.length !== 0);
+        
+        // check if we've got an error on our hands
+        if (submit === true && required === true && val.length === 0) {
+          error = true;
+          errorM.useRequiredMessage = true;
+        } else if (gotTests) {
+          for (var i = 0; i < opts.tests.length; i++){
+            if (isFunction(opts.tests[i].test) && !opts.tests[i].test(val, arg)){
+              errorM.message = opts.tests[i].message;
+              error = true;
+              break;
+            }
+          }
+        }
+        
+        if (error) {
+          el.addClass('unhappy');
+          errorMessaging(errorM, el);
+          return false;
+        } else {
+          el.removeClass('unhappy');
+          $("#"+field.attr('name') + '_unhappy').remove();
+          return true;
+        }
+      };
+
+      field.on(config.when || 'blur', field.testValid);
+      return field;
+    }
+    
+    if (config.submitButton) {
+      $(config.submitButton).on("click", handleSubmit);
+    } else {
+      
+      this.bind('submit', handleSubmit);
+    }
+    return this;
+  };
+})(this.jQuery || this.Zepto);
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/resources.js b/src/fauxton/app/addons/replicator/resources.js
new file mode 100644
index 0000000..1f93bc0
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/resources.js
@@ -0,0 +1,230 @@
+// 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',
+  "addons/documents/resources"
+],
+
+function (app, FauxtonAPI, ActiveTasks, Documents) {
+  var Replication = {};
+
+  //these are probably dupes from the database addon. I'm going to keep them seperate for now.
+
+  //Extend from top level document instead
+  Replication.DBModel = Backbone.Model.extend({
+    url: function(){
+      return app.host + "/" + this.id;
+    },
+    label: function () {
+      //for autocomplete
+        return this.get("name");
+    },
+    authValidation: function(password){
+      var username = app.session.get('userCtx').name,
+          that = this;
+      return $.ajax({
+        cache: false,
+        type: "POST", 
+        url: "/_session", 
+        dataType: "json",
+        data: {name: username, password: password},
+        success: function(){
+          that.passworderror = false;
+        },
+        error: function(resp){
+          that.passworderror = true;
+        }
+      });
+    }
+  });
+
+
+//extend alldocs funky, but theoretically 
+  Replication.DBList = Backbone.Collection.extend({
+    model: Replication.DBModel,
+    url: function() {
+      return app.host + "/_all_dbs";
+    },
+    getFiltered: function(dbname){
+      var filtered = this.filter(function(rep) {
+          return rep.get("name") === dbname;
+          });
+      return new Replication.DBList(filtered);
+    },
+    getReplicatorDB: function(){
+      return this.getFiltered("_replicator");
+    },
+    createReplicatorDB: function(){
+      var db = new this.model();
+      return db.save({
+        id: "_replicator",
+        name: "_replicator"
+      });
+    },
+    parse: function(resp) {
+      // TODO: pagination!
+      return _.map(resp, function(database) {
+        return {
+          id: database,
+          name: database
+        };
+      });
+    }
+  });
+
+
+
+//extend from active tasks
+  Replication.Task = Backbone.Model.extend({});
+
+  Replication.Tasks = Backbone.Collection.extend({
+    model: Replication.Task,
+    url: function () {
+      return app.host + '/_active_tasks';
+    },
+    fetch: function (options) {
+     var fetchoptions = options || {};
+     fetchoptions.cache = false;
+     return Backbone.Model.prototype.fetch.call(this, fetchoptions);
+    },
+    parse: function(resp){
+      //only want replication tasks to return
+      return _.filter(resp, function(task){
+        return task.type === "replication";
+      });
+    }
+  });
+
+  Replication.Replicate = Backbone.Model.extend({
+    idAttribute: 'id',
+    documentation: "replication_doc",
+    url: function(){
+      var docid = this.get('id') ? "/"+this.get('id'):"";
+      return app.host + "/_replicator"+docid;
+    },
+    destroy: function() {
+      var url = this.url() + "?rev=" + this.get('_rev');
+      return $.ajax({
+        url: url,
+        dataType: 'json',
+        type: 'DELETE'
+      });
+    }
+  });
+
+  Replication.item = Backbone.Model.extend({
+    defaults: {
+      source: "Source is missing",
+      target: "Target is missing",
+      create_target: false,
+      continuous: false,
+      doc: {}
+    },
+    initialize: function(){
+
+      var regex = "^(http|https|ftp)://([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&amp;%$-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|([a-zA-Z0-9-]+.)*[a-zA-Z0-9-]+.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(/($|[a-zA-Z0-9.,?'\\+&amp;%$#=~_-]+))*$";
+      this.urlregex = new RegExp(regex);
+    },
+    hasSource: function(){
+      //has a source
+      return this.get("source") !== this.defaults.source;
+    },
+    hasTarget: function(){
+      //has a target 
+      return this.get("target") !== this.defaults.target;
+    },
+    hasUserCtx: function(){
+      //has username & has roles
+      return this.get("user_ctx") === undefined;
+    },
+    isBoolean: function(test){
+      return typeof test === "boolean";
+    },
+    isURL: function(type){
+      var checkType = type || "target",
+          url = this.get(checkType);
+      return this.urlregex.test(url);
+    },
+    isLocal: function(type){
+      var checkType = type || "target",
+          thisURL = this.get(checkType);
+        
+      return (thisURL.indexOf(app.host) !== -1);
+    },
+
+    getDataforReplication:  function(){
+      return {
+        source: this.get('doc').source,
+        target: this.get('doc').target,
+        create_target: this.get('create_target'),
+        user_ctx: this.get('user_ctx'),
+        continuous: this.get('continuous')
+      };
+    }
+  });
+
+  Replication.Replicator = Backbone.Collection.extend({
+    model: Replication.item,
+    idAttribute: '_id',
+    documentation: "replication_doc",
+    url: function(){
+      return app.host + "/_replicator/_all_docs?limit=100&include_docs=true";
+    },
+    getfiltered: function(type){
+      var filtered = this.filter(function(rep) {
+          return rep.get("_replication_state") === type;
+          });
+      return new Replication.Replicator(filtered);
+    },
+    getCompleted: function(){
+      return this.getfiltered("completed");
+    },
+    getErrored: function(){
+      return this.getfiltered("error");
+    },
+    getURL: function(source){
+      if (typeof source == "object") {
+        return source.url;
+      } else {
+        return source;
+      }
+    },
+    parse: function(resp){
+      var rows = resp.rows,
+          getSource = this.getURL;
+
+      return _.map(rows, function(row) {
+        var source = getSource(row.doc.source),
+            target = getSource(row.doc.target);
+
+        return {
+          _id: row.id,
+          _rev: row.value.rev,
+          value: row.value,
+          key: row.key,
+          create_target: row.doc.create_target || undefined,
+          continuous: row.doc.continuous || undefined,
+          _replication_state:  row.doc._replication_state,
+          source: source || undefined,
+          target: target || undefined,
+          doc: row.doc,
+          user_ctx: row.value.user_ctx
+        };
+      });
+    }
+  });
+
+
+  return Replication;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/route.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/route.js b/src/fauxton/app/addons/replicator/route.js
new file mode 100644
index 0000000..85d601e
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/route.js
@@ -0,0 +1,101 @@
+// 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/replicator/resources",
+  "addons/replicator/views"
+],
+function(app, FauxtonAPI, Replication, Views) {
+  var  RepRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "with_sidebar",
+    roles: ["_admin"],
+    routes: {
+      "replication": "showSection",
+      "replication/:section": "showSection",
+      "replication/:section/:source": "showSection"
+    },
+    selectedHeader: "Replication",
+    apiUrl: function() {
+      return [this.replicate.url(), this.replicate.documentation];
+    },
+    crumbs: [
+      {"name": "Replication", "link": "replication"}
+    ],
+    initialize: function(){
+			this.databases = new Replication.DBList({});
+      this.tasks = new Replication.Tasks({id: "ReplicationTasks"});
+      this.replicate = new Replication.Replicate({});
+      this.replicatorDB = new Replication.Replicator({});
+      this.setView("#sidebar-content", new Views.Sidebar({}));  
+    },
+    showSection: function(section, source){
+      var maincontent;
+      $('.replication-nav-stacked').find('li').removeClass("active");
+      switch (section) {
+        case "new":
+          maincontent = new Views.ReplicationForm({
+            source: source || "",
+            collection: this.databases
+          });
+          this.crumbs = [
+            {"name": "Replication", "link": "replication"},
+            {"name": "Start a new replication", "link": "#"}
+          ];
+        break;
+        case "active":
+          maincontent = new Views.ActiveReplications({
+            collection: this.tasks
+          });
+          this.crumbs = [
+            {"name": "Replication", "link": "replication"},
+            {"name": "Active replications", "link": "#"}
+          ];
+        break;
+        case "errors":
+          maincontent = new Views.ReplicationErrors({
+            databases: this.databases,
+            collection: this.replicatorDB
+          });
+          this.crumbs = [
+            {"name": "Replication", "link": "replication"},
+            {"name": "Errors", "link": "#"}
+          ];
+        break;
+        case "complete":
+          maincontent = new Views.CompletedReplications({
+            databases: this.databases,
+            collection: this.replicatorDB
+          });
+          this.crumbs = [
+            {"name": "Replication", "link": "replication"},
+            {"name": "Completed", "link": "#"}
+          ];
+        break;
+        default:
+          maincontent = new Views.ReplicationForm({
+            source: source || "",
+            collection: this.databases
+          });
+        break;
+      }
+      $('a[data-type-select="'+section+'"]').parents('li').addClass('active');
+      this.setView("#dashboard-content", maincontent); 
+    }
+  });
+
+
+	Replication.RouteObjects = [RepRouteObject];
+
+  return Replication;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/active.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/active.html b/src/fauxton/app/addons/replicator/templates/active.html
new file mode 100644
index 0000000..cb5bfa9
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/active.html
@@ -0,0 +1,41 @@
+<!--
+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.
+-->
+<td>
+	<a href="#database/_replicator/<%=docid%>">
+		<%=docid%>
+	</a>
+</td>
+
+<td>
+	<%=source%>
+</td> 
+
+<td>
+	<%=target%>
+</td>
+
+<td>
+	<span class='<% if (continuous){%>icon-refresh <% } %>'>
+			<%=continuous%>
+	</span>
+</td>
+
+
+<td>
+	<%=docs_written|| "0"%> docs written
+</td>
+
+<td>
+	<button class="cancel button red fonticon-circle-x delete" data-source="<%=source%>"  data-doc-id="<%=docid%>" data-rep-id="<%=repid%>" data-continuous="<%=continuous%>" data-target="<%=target%>">Cancel</a>
+</td>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/complete.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/complete.html b/src/fauxton/app/addons/replicator/templates/complete.html
new file mode 100644
index 0000000..b946082
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/complete.html
@@ -0,0 +1,43 @@
+<!--
+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.
+-->
+<td>
+	<a href="#database/_replicator/<%=docid%>">
+		<%=docid%>
+	</a>
+</td>
+
+<td>
+	<%=source%>
+</td> 
+
+<td>
+	<%=target%>
+</td>
+
+<td>
+	<span class='<% if (continuous){ %>icon-refresh <% } %>'>
+			<%=continuous%>
+	</span>
+</td>
+
+
+<td>
+	<span class="green fonticon-check">
+		<%=status%>
+	</span>
+</td>
+
+<td>
+	<button class="retry button green fonticon-replicate " data-source="<%=source%>"  data-doc-id="<%=docid%>" data-rep-id="<%=repid%>" data-continuous="<%=continuous%>" data-target="<%=target%>">Again?</a>
+</td>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/error.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/error.html b/src/fauxton/app/addons/replicator/templates/error.html
new file mode 100644
index 0000000..5500079
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/error.html
@@ -0,0 +1,41 @@
+<!--
+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.
+-->
+<td>
+	<a href="#database/_replicator/<%=docid%>">
+		<%=docid%>
+	</a>
+</td>
+
+<td>
+	<%=source%>
+</td> 
+
+<td>
+	<%=target%>
+</td>
+
+<td>
+	<span class='<% if (continuous){%>icon-refresh <% } %>'>
+			<%=continuous%>
+	</span>
+</td>
+
+
+<td>
+	<button class="validate button red fonticon-x " data-source="<%=source%>"  data-doc-id="<%=docid%>" data-rep-id="<%=repid%>" data-continuous="<%=continuous%>" data-target="<%=target%>">Find Errors</a>
+</td>
+
+<td>
+	<button class=" button green fonticon-replicate retry" data-source="<%=source%>"  data-doc-id="<%=docid%>" data-rep-id="<%=repid%>" data-continuous="<%=continuous%>" data-target="<%=target%>">Retry</a>
+</td>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/form.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/form.html b/src/fauxton/app/addons/replicator/templates/form.html
new file mode 100644
index 0000000..754e8ae
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/form.html
@@ -0,0 +1,71 @@
+<!--
+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="control-group" id="step4">
+		<label class="control-label">_id <br>
+			<small>(optional)</small>
+		</label>
+		<div class="source controls">
+			<input type="text" id="repID" name="_id" size="30" value="" data-validation="optional" placeholder="e.g. my_rep">
+		</div>
+	</div>
+	<!-- SOURCE -->
+	<div class="control-group" >
+		<label class="control-label">SOURCE DATABASE:</label>
+		<div class="source controls">
+			<div id="source_form"></div>
+		</div>
+
+	</div>
+
+	<div class="control-group">
+		<label class="control-label">TARGET DATABASE:</label>
+		<div class="target controls">
+			<div class="btn-group toggle-btns" id="create_target">
+
+				<label for="existing-target" class="btn active">
+					Existing Database  
+				</label>
+				
+				<label for="new-target" class="btn">
+					New Database
+				</label>
+
+				<input type="radio" id="existing-target" name="create_target" class="next" checked="checked" value="false">
+				<input type="radio" id="new-target" name="create_target" class="next" value="true">
+	
+			</div>
+		</div>
+	</div>
+
+	<!--TARGET-->
+	<div class="control-group">
+		<div class="target controls">
+			<div id="target_form"></div>
+			<label for="continuous">
+				<input type="checkbox" name="continuous" value="true" id="continuous">
+				Make this replication continuous.
+			</label>
+		</div>
+	</div>
+
+	<div class="control-group">
+		<div class="actions target controls">
+			<button class="button green save btn-large fonticon-replicate" type="submit" >Replicate</button>
+		</div>
+	</div>
+
+</form>
+
+<div class="password_modal"></div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/localremotetabs.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/localremotetabs.html b/src/fauxton/app/addons/replicator/templates/localremotetabs.html
new file mode 100644
index 0000000..a87241d
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/localremotetabs.html
@@ -0,0 +1,39 @@
+<!--
+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.
+-->
+
+<ul class="nav nav-tabs" id="<%=type%>Tabs">
+  <li class="active">
+  	<a href="#" class="btn local-btn" data-bypass="true" data-tab="<%=type%>_local">
+  		<% if (newDB){%>Create a new database locally <%}else{%> My Databases <%}%>
+  	</a>
+  </li>
+  <li>
+  	<a  href="#" class="btn remote-btn" data-bypass="true" data-tab="<%=type%>_remote">
+  		<% if (newDB){%>Create a new remote database <%}else{%> Remote Database <%}%>
+  	</a>
+  </li>
+</ul>
+
+<div class="tab-content small-tabs">
+	<div class="tab-pane active" id="<%=type%>_local">
+		<input type="text" id="to_name" name="<%=type%>" autofocus size="30" placeholder="<%if(!newDB){%>Start typing to select a <%=type%> database<%}else{%>Name your database<%}%>" class="<%if(!newDB){%>auto<%}%>">
+	</div>
+
+	<div class="tab-pane" id="<%=type%>_remote">
+		<input type="text" id="<%=type%>_url" name="<%=type%>" size="30" class="next" placeholder="http://" >
+		<small>e.g. http://username:password@cloudant.com/database</small>
+	</div>
+</div>
+
+<div id="options-here"></div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/nope.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/nope.html b/src/fauxton/app/addons/replicator/templates/nope.html
new file mode 100644
index 0000000..50cd6c6
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/nope.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.
+-->
+<td colspan="5">
+You have no <%=type%> replications
+</td>
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/options.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/options.html b/src/fauxton/app/addons/replicator/templates/options.html
new file mode 100644
index 0000000..6bbf5d5
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/options.html
@@ -0,0 +1,35 @@
+<!--
+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="options off">Advanced Options</span>
+<div class="advancedOptions hide">
+	<h4>Apply filters</h4>
+	<p>Sometimes you don't want to transfer all documents from source to target. You can include one or more filter functions in a design document on the source and then tell the replicator to use them.</p>
+	<label for="filter">Enter the design doc and filter name</label>
+	<input type="text" placeholder="myddoc/myfilter" name="filter" id="filter"/>
+	<label for="query">Add query parameters (optional)</label>
+	<input type="text" placeholder='{"key":"value"}' name="query_params" id="query"/>
+
+	<hr>
+	<h4>Named Document Replication</h4>
+	<p>Sometimes you only want to replicate some documents. For this simple case you do not need to write a filter function. Simply add the list of keys, separated by commas.</p>
+	<input type="text" placeholder="foo, bar, baz" name="doc_ids" id="doc_ids"/>
+
+	<hr>
+	<h4>Replicate through a proxy</h4>
+	<p>Pass a "proxy" argument in the replication data to have replication go through an HTTP proxy</p>
+	<input type="text" placeholder="http://localhost:8888" name="proxy" id="proxy"/>
+
+</div>
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/password_modal.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/password_modal.html b/src/fauxton/app/addons/replicator/templates/password_modal.html
new file mode 100644
index 0000000..bf02fd3
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/password_modal.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 class="modal-header">
+  <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+  <h3>Password: </h3>
+</div>
+<div class="modal-body">
+  <p>Replication requires authentication.</p>
+
+    <input name="password" type="password" placeholder="Enter your password"/>
+
+</div>
+<div class="modal-footer">
+  <a class="button green continue-button" data-bypass="true">Continue Replication</a>
+  <a data-dismiss="modal" class="button cancel-button outlineGray" data-bypass="true"> Cancel</a>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/sidebar_no_header.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/sidebar_no_header.html b/src/fauxton/app/addons/replicator/templates/sidebar_no_header.html
new file mode 100644
index 0000000..83a01f9
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/sidebar_no_header.html
@@ -0,0 +1,20 @@
+<!--
+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="primary-navbar"></div>
+<div id="dashboard" class="container-fluid supportDash">
+  <div class="with-sidebar content-area">
+    <div id="sidebar-content" class="sidebar"></div>
+    <div id="dashboard-content" class="list window-resizeable"></div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/sidebartabs.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/sidebartabs.html b/src/fauxton/app/addons/replicator/templates/sidebartabs.html
new file mode 100644
index 0000000..d668c6a
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/sidebartabs.html
@@ -0,0 +1,30 @@
+<!--
+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.
+-->
+
+ <ul class="nav nav-tabs nav-stacked replication-nav-stacked">
+ 	<li>
+ 		<a data-type-select="new" href="#replication/new" id="case-button" class="button blue round-btn fonticon-circle-plus"> 
+			New Replication
+		</a>
+	</li>
+	<li>
+		<a data-type-select="active" href="#replication/active">Active Replications</a>
+	</li>
+	<li>
+		<a data-type-select="errors" href="#replication/errors">Errors</a>
+	</li>
+	<li>
+		<a data-type-select="complete" href="#replication/complete">Completed Replications</a>
+	</li>
+</ul>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/statustable.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/statustable.html b/src/fauxton/app/addons/replicator/templates/statustable.html
new file mode 100644
index 0000000..70ab5ac
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/statustable.html
@@ -0,0 +1,23 @@
+<!--
+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.
+-->
+  <thead>
+  	<th>Doc ID</th>
+    <th>Source</th>
+    <th>Target</th>
+    <th>Continuous?</th>
+    <th>Status</th>
+    <th>Actions</th>
+  </thead>
+  <tbody id="replication-status">
+  </tbody>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/templates/validator.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/templates/validator.html b/src/fauxton/app/addons/replicator/templates/validator.html
new file mode 100644
index 0000000..8980bc0
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/templates/validator.html
@@ -0,0 +1,125 @@
+<!--
+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.
+-->
+<td colspan="6">
+	<button class="close">&times;</button>
+	<p>Here are the basic issues that might have occurred. If your replication passes this test, look at it in the docs for other issues or contact us.</p>
+	<dl class="dl-horizontal replication-validator">
+
+		<dt>Does your replication have a Source?</dt>  
+		<dd class="validate-source">
+			<% if (source){ %> 
+				<span class="fonticon-circle-check green">YES</span> 
+			<% } else {%> 
+				<span class="fonticon-x red">NO</span>
+			<% } %>
+		</dd>
+
+		<% if (!model.isURL("source")){ %>
+			<dt>Is your source a full url?</dt>
+			<dd class="validate-target">
+					<span class="fonticon-x red"></span>
+					When passing in source and target the reference must be a full URL.  e.g:  <code>http://username.cloudant.com/dbname</code>
+			</dd>
+		<% } %>
+
+		<dt>Does your replication have a Target?</dt>
+		<dd class="validate-target">
+			<% if (target){ %> 
+				<span class="fonticon-circle-check green">YES</span> 
+			<% } else {%> 
+				<span class="fonticon-x red">NO</span>
+			<% } %>
+		</dd>
+
+
+
+		<% if (!model.isURL("target")){ %>
+			<dt>Is your target a full url?</dt>
+			<dd class="validate-target">
+					<span class="fonticon-x red"></span>
+					When passing in source and target the reference must be a full URL.  e.g:  <code>http://username.cloudant.com/dbname</code>
+			</dd>
+		<% } %>
+
+		<% if (!typeof model.get("create_target") === "boolean"){ %>
+			<dt>Create_target primitive type...</dt>
+			<dd class="validate-target">
+					<span class="fonticon-x red">Not boolean</span>
+					Suggestion: <code>"create_target": true</code> must be a boolean (true || false)
+			</dd>
+		<% } %>
+
+		<% if (!typeof model.get("continuous") === "boolean"){ %>
+			<dt>Continuous primitive type...</dt>
+			<dd class="validate-target">
+					<span class="fonticon-x red">Not boolean</span>
+					Suggestion: <code>"continuous": true</code> must be a boolean (true || false)
+			</dd>
+		<% } %>
+
+		<dt>Does your replication have a user_ctx?</dt>
+		<dd class="validate-user">
+			<% if (user_ctx){ %> 
+				<span class="fonticon-circle-check green">YES</span> 
+			<% } else {%> 
+				<span class="fonticon-x red">NO</span>
+				The user_ctx property is mandatory for cloudant. It is provided by default when submitting a replication in the client. 
+				Example: 
+				<pre>
+				{
+				  "_id": "my_rep",
+				  "source":  "http://bserver.com:5984/foo",
+				  "target":  "bar",
+				  "continuous":  true,
+				  "user_ctx": {
+				      "name": "joe",
+				      "roles": ["erlanger", "researcher"]
+				  }
+				}
+				</pre>
+			<% } %>
+		</dd>
+
+	</dl>
+
+	<p>If everything passed, here are additional things to check: </p>
+	<dl class="dl-horizontal replication-validator">
+		<dt>Have you authenticated?</dt>
+		<dd><p>In order to access target and source databases, you must authenticate with the proper username &amp; password.  You can authenticate in the database url, e.g. <code>http://username:password@username.cloudant.com/foo</code></p>
+
+
+		<p>Or when posting to _replicator, include headers if you wish to encrypt your passwords. </p>
+<pre>
+{
+	//...
+	source: {
+		url: "http://username.cloudant.com/foo",
+		headers: {
+			"Authorization": "BASIC dXNlcm5hbWU6cGFzc3dvcmQ="
+		}
+	}
+}
+</pre>
+		</dd>
+
+		<dt>Make sure remote databases exist: </dt>
+		<dd>Replicating from a database that isn't hosted under this account? Make sure that the database exists.</dd>
+
+
+
+	</dl>
+
+
+
+</td>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/app/addons/replicator/tests/replicatorSpec.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/tests/replicatorSpec.js b/src/fauxton/app/addons/replicator/tests/replicatorSpec.js
new file mode 100644
index 0000000..702d7ec
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/tests/replicatorSpec.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/cloudantreplication/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/85c28725/src/fauxton/app/addons/replicator/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replicator/views.js b/src/fauxton/app/addons/replicator/views.js
new file mode 100644
index 0000000..2cb8342
--- /dev/null
+++ b/src/fauxton/app/addons/replicator/views.js
@@ -0,0 +1,776 @@
+// 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/fauxton/components",
+       "addons/replicator/resources",
+       "addons/replicator/happy"
+],
+function(app, FauxtonAPI, Components, replication) {
+  var View = {},
+  Events ={},
+  pollingInfo ={
+    rate: 5,
+    intervalId: null
+  },
+  retryReplication = {};
+
+  _.extend(Events, Backbone.Events);
+
+  View.Sidebar = FauxtonAPI.View.extend({
+    template: 'addons/replicator/templates/sidebartabs',
+    afterRender: function(){ 
+      var historyFrag = Backbone.history.fragment.replace("replication/",""),
+          thisSection = (historyFrag=="replication")?'new':historyFrag;
+      $(".replication-nav-stacked").find('.active').removeClass('active');
+      $("#sidebar-content").find("[data-type-select='"+thisSection+"']").parents('li').addClass("active");
+    }
+  });
+
+
+  View.ReplicationItem = FauxtonAPI.View.extend({
+    tagName: "tr",
+    template: "addons/replicator/templates/active",
+    events: {
+      "click .cancel": "cancelReplication"
+    },
+    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'),
+      docID = $currentTarget.attr('data-doc-id'),
+      that = this;
+
+      this.newRepModel = new replication.Replicate({
+        "id": docID,
+        "replication_id": repID,
+        "cancel": true
+      });
+      this.newRepModel.fetch().then(function(){
+        that.deleteit();
+      });
+    },
+    deleteit: function(){
+      this.newRepModel.destroy().then(function(model, xhr, options){
+          var notification = FauxtonAPI.addNotification({
+            msg: "Replication stopped.",
+            type: "success",
+            clear: true
+          });
+        },
+        function(model, xhr, options){
+          var errorMessage = JSON.parse(xhr.responseText);
+          var notification = FauxtonAPI.addNotification({
+            msg: errorMessage.reason,
+            type: "error",
+            clear: true
+          });
+        });
+    },
+    serialize: function(){
+      return {
+        docs_written: this.model.get('docs_written'),
+        target: this.model.get('target').replace(/\:(\w*)\b/,":*****"),
+        source: this.model.get('source').replace(/\:(\w*)\b/,":*****"),
+        continuous: this.model.get('continuous'),
+        repid: this.model.get('replication_id'),
+        docid: this.model.get('doc_id')
+      };
+    }
+  });
+
+  View.ActiveReplications = FauxtonAPI.View.extend({
+    tagName: "table",
+    className:  "replication-active replication-table table table-striped",
+    template: "addons/replicator/templates/statustable",
+    initialize:  function(){
+      Events.bind('update:tasks', this.establish, this);
+      this.listenTo(this.collection, "reset", this.render);
+    },
+    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(){
+      if (this.collection.length > 0 ) {
+        this.collection.forEach(function(item) {
+          this.insertView("#replication-status", new View.ReplicationItem({ 
+            model: item
+          }));
+        }, this);
+      } else {
+        this.insertView("#replication-status", new View.ReplicatorNone({
+          type: "active"
+        }));
+      }
+    },
+    afterRender: function(){
+      this.setPolling();
+    }
+  });
+
+
+  View.ReplicationErrors = FauxtonAPI.View.extend({
+    tagName: "table",
+    className:  "replication-errors  replication-table table table-striped",
+    template: "addons/replicator/templates/statustable",
+    initialize: function(options){
+      this.databases = options.databases;
+      this.replicatorDB = false;
+    },
+    establish: function(){
+      var deferred = FauxtonAPI.Deferred(),
+          that = this;
+
+      FauxtonAPI.when(this.databases.fetch()).always(function(resp) {
+        if (that.databases.getReplicatorDB().length > 0){
+
+          that.collection.fetch().done(function(){
+            that.replicatorDB = true;
+            deferred.resolve();
+          });
+        } else {
+          that.replicatorDB = false;
+          deferred.resolve();
+        }
+      });
+      return [deferred];
+    },
+    beforeRender: function(){
+      //put this into the collection
+      if (this.replicatorDB){
+        var errors = this.collection.getErrored();
+        if (errors.length > 0){
+          errors.forEach(function(item) {
+            //CAP R
+            this.insertView("#replication-status", new View.ReplicationError({ 
+              model: item
+            }));
+          }, this);
+        } else {
+          this.insertView("#replication-status", new View.ReplicatorNone({
+            type: "errored"
+          }));
+        }
+      } else {
+        this.insertView("#replication-status", new View.ReplicatorNone({
+          type: "errored"
+        }));
+      }
+    }
+  });
+
+
+  View.ReplicatorNone = FauxtonAPI.View.extend({
+    tagName: "tr",
+    template: "addons/replicator/templates/nope",
+    initialize:  function(options){
+      this.type = options.type;
+    },
+    serialize: function(){
+      return {
+        type:  this.type
+      };
+    }
+  });
+
+  View.ReplicationValidator = FauxtonAPI.View.extend({
+    tagName: "tr",
+    template: "addons/replicator/templates/validator",
+    events: {
+      "click .close": "remove" 
+    },
+    serialize: function(){
+      return {
+        source: this.model.hasSource(),
+        target: this.model.hasTarget(),
+        user_ctx: this.model.hasUserCtx(),
+        auth: false,
+        model: this.model
+      };
+    }
+  });
+
+
+  View.ReplicationError = FauxtonAPI.View.extend({
+    template: "addons/replicator/templates/error",
+    tagName: "tr",
+    events: {
+      "click button.validate": "validate",
+      "click button.retry": "retryRep"
+    },
+    retryRep:  function(){
+      retryReplication.source = {
+        url: this.model.get("source"),
+        local: this.model.isLocal("source")
+      };
+      retryReplication.target = {
+        url: this.model.get("target"),
+        local: this.model.isLocal("target")
+      };
+      FauxtonAPI.navigate('/replication/new');
+    },
+    validate: function(){
+      if (this.validateview){this.validateview.remove();}
+      this.validateview = new View.ReplicationValidator({
+        model:  this.model
+      });
+      this.$el.after(this.validateview.render().el);
+    },
+    serialize:  function(){
+      return {
+        status: this.model.get("_replication_state"),
+        target: this.model.get("target").replace(/\:(\w*)\b/,":*****"),
+        source: this.model.get("source").replace(/\:(\w*)\b/,":*****"),
+        continuous: this.model.get("continuous")||false,
+        repid: this.model.get("_id"),
+        docid: this.model.get("_id")
+      };
+    }
+  });
+
+  View.ReplicationComplete = FauxtonAPI.View.extend({
+    template: "addons/replicator/templates/complete",
+    tagName: "tr",
+    events: {
+      "click button.retry": "postReplication"
+    },
+    postReplication: function(){
+      var data = this.model.getDataforReplication(),
+      that = this;
+
+      this.newRepModel = new replication.Replicate({});
+      this.newRepModel.save(data,{
+        success: function(model, xhr, options){
+          var notification;
+          notification = FauxtonAPI.addNotification({
+            msg: "Replication from "+model.get('source').url+" to "+ model.get('target').url+" has been posted to the _replicator database.",
+            type: "success",
+            clear: true
+          });
+          model.fetch().done(
+            function(resp){ 
+              if (resp._replication_state === "error") {
+                notification = FauxtonAPI.addNotification({
+                  msg: "Replication error. Check the _replicator database for errors.",
+                  type: "error",
+                  clear: true
+                });
+                FauxtonAPI.navigate('/replication/active');
+              } else if (resp._replication_state === "triggered"){
+                notification = FauxtonAPI.addNotification({
+                  msg: "Replication has been triggered.",
+                  type: "success",
+                  clear: true
+                });
+              } else {
+                notification = FauxtonAPI.addNotification({
+                  msg: "This replication has been posted to the _replicator database but hasn't been fired yet. Check back in a few mins to see it's state.",
+                  clear: true
+                });
+              }
+            }
+          );
+        },
+        error: function(model, xhr, options){
+          var errorMessage = JSON.parse(xhr.responseText);
+          var notification = FauxtonAPI.addNotification({
+            msg: errorMessage.reason,
+            type: "error",
+            clear: true
+          });
+        }
+      });
+    },
+    serialize:  function(){
+      return {
+        status: this.model.get("_replication_state"),
+        target: this.model.get("target").replace(/\:(\w*)\b/,":*****"),
+        source: this.model.get("source").replace(/\:(\w*)\b/,":*****"),
+        continuous: this.model.get("continuous")||false,
+        repid: this.model.get("_id"),
+        docid: this.model.get("_id")
+      };
+    }
+  });
+
+  View.CompletedReplications = FauxtonAPI.View.extend({
+    tagName: "table",
+    className:  "replication-complete replication-table table table-striped",
+    template: "addons/replicator/templates/statustable",
+    initialize: function(options){
+      this.databases = options.databases;
+      this.replicatorDB = false;
+    },
+    establish: function(){
+      var deferred = FauxtonAPI.Deferred(),
+          that = this;
+
+      FauxtonAPI.when(this.databases.fetch()).always(function(resp) {
+        if (that.databases.getReplicatorDB().length > 0){
+
+          that.collection.fetch().done(function(){
+            that.replicatorDB = true;
+            deferred.resolve();
+          });
+        } else {
+          that.replicatorDB = false;
+          deferred.resolve();
+        }
+      });
+      return [deferred];
+    },
+    beforeRender: function(){
+      if (this.replicatorDB){
+        var completed = this.collection.getCompleted();
+        if (completed.length > 0){
+          completed.forEach(function(item) {
+            this.insertView("#replication-status", new View.ReplicationComplete({ 
+              model: item
+            }));
+          }, this);
+        } else {
+          this.insertView("#replication-status", new View.ReplicatorNone({
+            type: "completed"
+          }));
+        }
+      } else {
+        this.insertView("#replication-status", new View.ReplicatorNone({
+          type: "completed"
+        }));
+      }
+    }
+  });
+
+
+
+  View.ReplicationForm = FauxtonAPI.View.extend({
+    template: "addons/replicator/templates/form",
+    events:  {
+      "submit #replication": "getPassword",
+      "change #create_target input[type='radio']": "showTargetTabs",
+      "click #create_target label": "buttonActiveState"
+    },
+    initialize: function(options){
+      this.status = options.status;
+      this.selectedSource = options.source;
+      Events.bind('replicate:start', this.replicateStart, this);
+    },
+    afterRender: function(){
+      // client side form validation
+      this.formvalidation();
+      //focus on first input
+      $(this.el).find("input:visible").eq(0).focus();
+    },
+    beforeRender:  function(){
+      //insert the source and target tabs
+      var sourceDB = this.selectedSource;
+      this.insertView("#source_form", new View.LocalRemoteTabs({
+        selectedDB: sourceDB  || "",
+        type: "source"
+      }));
+      this.targetForm = this.insertView("#target_form", new View.LocalRemoteTabs({
+        type: "target"
+      }));
+
+    },
+    buttonActiveState: function(e){
+      //Set the button state on LOCAL or NEW
+      var $currentTarget = this.$(e.currentTarget);
+      $currentTarget.parents("#create_target").find('.active').removeClass('active');
+      $currentTarget.addClass('active');
+    },
+    enableFields: function(){
+      this.$el.find('input','select').attr('disabled',false);
+    },
+    disableFields: function(){
+      this.$el.find('input[type="text"]:hidden','select:hidden').not("[type='radio']").attr('disabled',true);
+    },
+    establish: function(){
+      return [this.collection.fetch()];
+    },
+    formvalidation: function(e){
+      //client side form validation
+      var that = this;
+      this.$('form#replication').isHappy({
+        fields: {
+          'input[name="source"]:visible': {
+            required: true,
+            tests: [
+              {
+                test: function(val){
+                  return $('input[name="target"]:visible').val() !== val;
+                },
+                message: 'Your source cannot be the same DB as your target',
+              },
+              {
+                test: function(val){
+                  if (that.$('input#source_url').is(':visible')){
+                    return true;
+                  } else {
+                    var alreadyExists = that.collection.where({"name":val});
+                    return alreadyExists.length !== 0;
+                  }
+                },
+                message: "This database doesn't exist."
+              },
+              {
+                test: function(val){
+                  if (!$('input[name="source"]:visible').is("#source_url")){
+                    return true;
+                  } else {
+                    var regex = "^(http|https|ftp)://([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&amp;%$-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|([a-zA-Z0-9-]+.)*[a-zA-Z0-9-]+.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(/($|[a-zA-Z0-9.,?'\\+&amp;%$#=~_-]+))*$",
+                    urlregex = new RegExp(regex);
+                    return urlregex.test(val);
+                  }
+                },
+                message: 'Remote databases must be written as urls.',
+              }
+            ]
+          },
+          'input[name="target"]:visible': {
+            required: true,
+            tests: [
+              {
+                test: function(val){
+                  return $('input[name="source"]:visible').val() !== val;
+                },
+                message: 'Your target cannot be the same DB as your source.'
+              },
+
+              {
+                test: function(val){
+                  var alreadyExists = that.collection.where({"name":val});
+                  if (that.$('input#target_url').is(':visible') ){
+                    return true;
+                  } else if (that.$('[name="create_target"]:checked').val()==="false") {
+                    return alreadyExists.length !== 0;
+                  } else {
+                    return alreadyExists.length === 0;
+                  }
+                },
+                message: "This database doesn't exist. Select New Database if you want to create it."
+              },
+              {
+                test: function(val){
+                  if (!$('input[name="target"]:visible').is("#target_url")){
+                    return true;
+                  } else {
+                    var regex = "^(http|https|ftp)://([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&amp;%$-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|([a-zA-Z0-9-]+.)*[a-zA-Z0-9-]+.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(/($|[a-zA-Z0-9.,?'\\+&amp;%$#=~_-]+))*$",
+                    urlregex = new RegExp(regex);
+                    return urlregex.test(val);
+                  }
+                },
+                message: 'Remote databases must be written as urls.',
+              }
+            ]
+          }
+        }
+      });
+    },
+    showTargetTabs: function(e){
+      //Switch from local to new
+      if (this.targetForm){ this.targetForm.remove();}
+      this.targetForm = this.insertView("#target_form", new View.LocalRemoteTabs({
+                          newDB: this.$('[name="create_target"]:checked').val()==="true",
+                          type: "target"
+                        }));
+      this.targetForm.render();
+    },
+    postToReplicator: function(json){
+      // Post to _replicator DB
+      var that = this;
+      this.newRepModel = new replication.Replicate({});
+      this.newRepModel.save(json,{
+        success: function(model, xhr, options){
+          var notification = FauxtonAPI.addNotification({
+            msg: "Replication from "+model.get('source').url+" to "+ model.get('target').url+" has been posted to the _replicator database.",
+            type: "success",
+            clear: true
+          });
+          that.updateButtonText(false);
+          that.checkReplicationState(model);
+        },
+        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();
+    },  
+    checkReplicationState: function(model){
+      // check if it's been triggered
+      // redirect to /replication/active if the replication has been triggered
+      var replicator = model,
+          notification;
+      replicator.fetch().done(
+        function(resp){ 
+          if (resp._replication_state === "error") {
+            notification = FauxtonAPI.addNotification({
+              msg: "Replication error. Check the _replicator database for errors.",
+              type: "error",
+              clear: true
+            });
+            FauxtonAPI.navigate('/replication/errors');
+          } else if (resp._replication_state === "triggered"){
+            notification = FauxtonAPI.addNotification({
+              msg: "Replication has been triggered.",
+              type: "success",
+              clear: true
+            });
+            FauxtonAPI.navigate('/replication/active');
+          } else {
+            notification = FauxtonAPI.addNotification({
+              msg: "This replication has been posted to the _replicator database but hasn't been fired yet. Check the _replicator DB to see it's state.",
+              clear: true
+            });
+          }
+        }
+      );
+    },
+    updateButtonText: function(showWaitingText){
+      var $button = this.$('#replication button[type=submit]');
+      if(showWaitingText){
+        $button.text('Starting replication...').attr('disabled', true);
+      } else {
+        $button.text('Replication').attr('disabled', false);
+      }
+    },
+
+    replicateStart: function(password){
+      var formData = this.scrubFormData(),
+          username = app.session.get('userCtx').name;
+      if ($('#source_local').is(':visible')){
+        formData.source = this.setAuthHeaders(formData.source,username,password);
+      } 
+      if ($('#target_local').is(':visible')){
+        formData.target = this.setAuthHeaders(formData.target,username,password);
+      }
+
+      this.submit(formData);
+    },
+    getPassword: function(e){
+      e.preventDefault();
+      this.updateButtonText(true);
+      var formData = this.scrubFormData();
+      
+      if ($('#source_local').is(':visible') || $('#target_local').is(':visible')){
+         this.passwordPopup();
+      } else {
+        this.submit(formData);
+      }
+    },
+
+    submit: function(formData){
+      var that = this;
+      if (this.collection.getReplicatorDB().length === 0){
+        FauxtonAPI.when(this.collection.createReplicatorDB()).always(function(resp){
+          that.postToReplicator(formData);
+          that.enableFields();
+        });
+      }else{
+        this.postToReplicator(formData);
+        this.enableFields();
+      }
+     
+ 
+     
+    },
+    setAuthHeaders: function(source,user,pass){
+      var basicHeader = new FauxtonAPI.session.createBasicAuthHeader(user,pass),
+          json = {};
+          json.url = window.location.origin +"/"+ app.utils.safeURLName(source);
+          json.headers = {
+              "Authorization": basicHeader.basicAuthHeader
+          };
+      return json;
+    },
+    passwordPopup: function(){ 
+      //insert Password Modal 
+      var AuthenticationView = FauxtonAPI.getExtensions('replicator:Authentication');
+
+      if (AuthenticationView){
+      var model = new this.collection.model(),
+          password = this.insertView("#password_modal", new AuthenticationView({
+                        model: model
+                      }));
+          password.render();
+      } else {
+        this.submit(formData);
+      }
+    },
+    scrubFormData: function(){
+      //DISABLE HIDDEN FIELDS
+      this.disableFields();
+      var data = {};
+      _.map(this.$('#replication').serializeArray(), function(formData){
+        if(formData.value !== ''){
+          //clean booleans & whitespaces
+          if (formData.name == "_id"){
+            data[formData.name]=formData.value.replace(/\s/g, '').toLowerCase();
+          } else if (formData.name == "create_target" || formData.name == "continuous"){
+              data[formData.name] = (formData.value ==="true")?true:false;
+          } else {
+          //Lotta stuff needs to be scrubbed before it's in proper json to submit
+            data[formData.name] = formData.value.trim().replace(/\s/g, '-');
+          }
+        }
+      });
+      data.user_ctx = FauxtonAPI.session.get('userCtx');
+      return data;
+    }
+  });
+
+  View.PasswordModal = FauxtonAPI.View.extend({
+    tagName: "div",
+    className: "modal",
+    template: 'addons/replicator/templates/password_modal',
+    events: {
+      "click a.cancel-button" : "hideModal",
+      "click a.continue-button": "checkValidation"
+    },
+    initialize:  function(){
+      this.showModal();
+    },
+    triggerReplication: function(){
+      Events.trigger('replicate:start', this.$('[name="password"]').val());
+    },
+    checkValidation: function(e){
+      e.preventDefault();
+      var password = this.$('[name="password"]').val(),
+          that = this;
+      FauxtonAPI.when(this.model.authValidation(password)).always(function(){
+        if (that.model.passworderror){
+          var notification = FauxtonAPI.addNotification({
+            msg: "Your password is incorrect.",
+            type: "error",
+            clear: true
+          });
+        } else {
+          that.triggerReplication();
+          that.hideModal();
+        }
+      });
+    },
+    hideModal: function(e){
+      if(e){e.preventDefault();}
+      $(this.el).modal('hide');
+    },
+    showModal: function(){
+      $(this.el).modal({show:true});
+    }
+  });
+
+  View.AdvancedOptions = FauxtonAPI.View.extend({
+    className: "authenticate",
+    template: "addons/replicator/templates/options",
+    events: {
+      "click .options": "toggleAdvancedOptions",
+    },
+    toggleAdvancedOptions:  function(e){
+      this.$(e.currentTarget).toggleClass("off");
+      this.$('.advancedOptions').toggle("hidden").find('input').removeAttr('disabled');
+    }
+  });
+
+  View.LocalRemoteTabs = FauxtonAPI.View.extend({
+    template: "addons/replicator/templates/localremotetabs",
+    events:  {
+      "click .nav-tabs a": "tabs"
+    },
+    afterRender: function(){
+      this.dbSearchTypeahead = new Components.DbSearchTypeahead({
+        dbLimit: 30,
+        el: "input.auto",
+        updater: function(item){
+          return item;
+        }
+      });
+      this.dbSearchTypeahead.render();
+      this.preselectedDatabase();
+    },
+    initialize: function(options){
+      this.type = options.type;
+      this.newDB = options.newDB || false;
+      this.selected = options.selectedDB || "";
+    },
+
+    preselectedDatabase: function(){
+      //if selected database is passed through from the _all_dbs page
+      if (this.selected){
+        this.$('input[type="text"]:visible').val(this.selected);
+      } else if (retryReplication[this.type]) {
+        this.setLocation();
+      }
+    },
+    setLocation: function(){
+      if (retryReplication[this.type].local){
+        var urlArray = retryReplication[this.type].url.split('/'),
+            location = urlArray[urlArray.length-1];
+        this.$('input[type="text"]:visible').val(location);
+      } else {
+        this.$('.remote-btn').trigger("click");
+        this.$('input[type="text"]:visible').val(retryReplication[this.type].url);
+      }
+      delete retryReplication[this.type];
+    },
+    showAdvancedOptions:  function(e){
+      //not called for now
+      if (this.$(e.currentTarget).attr('name') === "source"){
+        if (this.advancedOptions){ this.advancedOptions.remove();}
+          this.advancedOptions = this.insertView("#options-here", new View.AdvancedOptions({}));
+          this.advancedOptions.render();
+      }
+    },
+    tabs: function(e){
+      e.preventDefault();
+      var $currentTarget = this.$(e.currentTarget),
+          getTabID = "#"+$currentTarget.attr('data-tab');
+
+      $currentTarget.parents('ul').find('.active').removeClass('active');
+      $currentTarget.parents('li').addClass('active');
+
+      $(getTabID).parents('.tab-content').find('.active').removeClass('active');
+      $(getTabID).addClass('active');
+    },
+    serialize: function(){
+      return {
+        newDB:  this.newDB,
+        username: app.session.get('userCtx').name,
+        type: this.type
+      };
+    }
+  });
+
+
+  return View;
+});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/85c28725/src/fauxton/settings.json.default
----------------------------------------------------------------------
diff --git a/src/fauxton/settings.json.default b/src/fauxton/settings.json.default
index 1bc88f6..bbf8016 100644
--- a/src/fauxton/settings.json.default
+++ b/src/fauxton/settings.json.default
@@ -8,6 +8,7 @@
   { "name": "config" },
   { "name": "logs" },
   { "name": "stats" },
+  { "name": "replicator" },
   { "name": "replication" },
   { "name": "plugins" },
   { "name": "contribute" },