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/10/01 15:46:58 UTC

[48/50] [abbrv] git commit: updated refs/heads/replicator-redesign to 066d69b

New Replication UI and switching to _replicator


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

Branch: refs/heads/replicator-redesign
Commit: 62e6dacca9629c591316b5790aa8f8c5921870e2
Parents: b144050
Author: suelockwood <de...@gmail.com>
Authored: Thu Sep 26 10:38:10 2013 -0400
Committer: suelockwood <de...@gmail.com>
Committed: Mon Sep 30 16:25:46 2013 -0400

----------------------------------------------------------------------
 .../replication/assets/less/replication.less    |  50 +++
 src/fauxton/app/addons/replication/resources.js |   7 +-
 src/fauxton/app/addons/replication/route.js     |   4 +-
 .../replication/templates/authfields.html       |  16 +
 .../app/addons/replication/templates/form.html  |  87 ++---
 .../replication/templates/localremotetabs.html  |  35 ++
 .../replication/templates/newdatabase.html      |  34 ++
 .../addons/replication/templates/options.html   |  36 ++
 src/fauxton/app/addons/replication/views.js     | 325 +++++++++++++++----
 .../app/templates/layouts/one_pane_notabs.html  |  27 ++
 src/fauxton/assets/js/plugins/happy.js          | 150 +++++++++
 11 files changed, 654 insertions(+), 117 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/62e6dacc/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
index a301966..ea446ec 100644
--- a/src/fauxton/app/addons/replication/assets/less/replication.less
+++ b/src/fauxton/app/addons/replication/assets/less/replication.less
@@ -25,6 +25,54 @@ form#replication {
 	max-width: none;
 	width: auto;
 
+	#create_target{
+		input[type=radio]{display: none;}
+		label.btn {
+			margin-right: 0;
+		}
+	}
+
+	.actions{
+		padding: 15px 0;
+	}
+	#from_name {
+		margin-right: 20px;
+	}
+	.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{
+		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: 30px 20px;
+		}
+		> .active {
+			display: block;
+			background-color: #fff;
+		}
+	}
+	h3 {
+		margin-top: 0;
+		line-height: 27px
+	}
 	.form_set{
 		width: 350px;
 		display: inline-block;
@@ -157,6 +205,7 @@ form#replication {
 
 }
 #replicationStatus{
+
 	&.showHeader{
 		li.header{
 			display: block;
@@ -194,3 +243,4 @@ form#replication {
 		}
 	}
 }
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/62e6dacc/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
index 38ae139..8c916c4 100644
--- a/src/fauxton/app/addons/replication/resources.js
+++ b/src/fauxton/app/addons/replication/resources.js
@@ -21,6 +21,9 @@ function (app, FauxtonAPI, ActiveTasks) {
 
   //these are probably dupes from the database modules. I'm going to keep them seperate for now.
   Replication.DBModel = Backbone.Model.extend({
+    url: function(){
+      return app.host + "/" + this.id;
+    },
     label: function () {
       //for autocomplete
         return this.get("name");
@@ -60,9 +63,11 @@ function (app, FauxtonAPI, ActiveTasks) {
 
   Replication.Replicate = Backbone.Model.extend({
     url: function(){
-      return app.host + "/_replicate";
+      return app.host + "/_replicator";
     }
   });
 
+
+
   return Replication;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/62e6dacc/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
index 7ea318c..0dd25a5 100644
--- a/src/fauxton/app/addons/replication/route.js
+++ b/src/fauxton/app/addons/replication/route.js
@@ -18,7 +18,7 @@ define([
 ],
 function(app, FauxtonAPI, Replication, Views) {
   var  RepRouteObject = FauxtonAPI.RouteObject.extend({
-    layout: "one_pane",
+    layout: "one_pane_notabs",
     roles: ["_admin"],
     routes: {
       "replication": "defaultView",
@@ -29,7 +29,7 @@ function(app, FauxtonAPI, Replication, Views) {
       return app.host+"/_replication";
     },
     crumbs: [
-      {"name": "Replicate changes from: ", "link": "replication"}
+      {"name": "", "link": "replication"}
     ],
     defaultView: function(dbname){
 			this.databases = new Replication.DBList({});

http://git-wip-us.apache.org/repos/asf/couchdb/blob/62e6dacc/src/fauxton/app/addons/replication/templates/authfields.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/templates/authfields.html b/src/fauxton/app/addons/replication/templates/authfields.html
new file mode 100644
index 0000000..9806dcf
--- /dev/null
+++ b/src/fauxton/app/addons/replication/templates/authfields.html
@@ -0,0 +1,16 @@
+<!--
+Licensed under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
+-->
+<p>Authorize the use of this database for replication.</p>
+<input class="input-medium" type="text" name="user_<%=type%>" size="30" placeholder="Username">
+<input class="input-medium next" type="password" name="password_<%=type%>" size="30" placeholder="Password" data-next-step="step<%=step%>">

http://git-wip-us.apache.org/repos/asf/couchdb/blob/62e6dacc/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
index 32a87dc..e75453e 100644
--- a/src/fauxton/app/addons/replication/templates/form.html
+++ b/src/fauxton/app/addons/replication/templates/form.html
@@ -11,64 +11,67 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 License for the specific language governing permissions and limitations under
 the License.
 -->
-
 <form id="replication" class="form-horizontal">
-		<div class="from form_set  local">
-			<div class="btn-group">
-			  <button class="btn local-btn" type="button" value="local">Local</button>
-			  <button class="btn remote-btn" type="button" value="remote">Remote</button>
-			</div>
-
-			<div class="from_local local_option">
-				<select id="from_name" name="source">
-					<% _.each( databases, function( db, i ){ %>
-					   <option value="<%=db.name%>" <% if(selectedDB == db.name){%>selected<%}%> ><%=db.name%></option>
-					<% }); %>
-				</select>
-			</div>
-			<div class="from_to_remote remote_option">
-				<input type="text" id="from_url" name="source" size="30" value="http://">
-			</div>
+	<!-- SOURCE -->
+	<div class="control-group" id="step1">
+		<label class="control-label">FROM: </label>
+		<div class="source controls">
+			<p>Select a database to replicate.</p>
+			<div id="source_form"></div>
 		</div>
 
-		<div class="form_set middle">
-			<span class="circle "></span>
-				<a href="#" title="Switch Target and Source" class="swap">
-					<span class="fonticon-swap-arrows"></span>
-				</a>
-			</span>
-		</div>
+	</div>
 
-		<div class="to form_set local">
-			<div class="btn-group">
-			  <button class="btn local-btn" type="button" value="local">Local</button>
-			  <button class="btn remote-btn" type="button" value="remote">Remote</button>
-			</div>
-			<div class="to_local local_option">
-				<input type="text" id="to_name" name="target" size="30" placeholder="database name">
-			</div>
+	<div class="control-group hide" id="step2">
+		<label class="control-label">TO:</label>
+		<div class="target controls">
+			<p>Where do you want to replicate your data?</p>
+			<div class="btn-group" id="create_target">
+
+				<label for="existing-target" class="btn">
+					Existing Database  
+				</label>
+				
+				<label for="new-target" class="btn">
+					New Database
+				</label>
 
-			<div class="to_remote remote_option">
-				<input type="text" id="to_url" name="target" size="30" value="http://">
+				<input type="radio" id="existing-target" name="create_target" class="next" data-next-step="step3" value="false">
+				<input type="radio" id="new-target" name="create_target" class="next" data-next-step="step3" value="true">
+	
 			</div>
 		</div>
+	</div>
 
+	<!--TARGET-->
+	<div class="control-group hide" id="step3">
+		<div class="target controls">
+			<p>Select a target database for your data.</p>
+			<div id="target_form"></div>
 
-	<div class="actions">
-		<div class="control-group">
 			<label for="continuous">
 				<input type="checkbox" name="continuous" value="true" id="continuous">
-				Continuous
+				Make this replication continuous.
 			</label>
 
-			<label for="createTarget">
-				<input type="checkbox" name="create_target" value="true" id="createTarget">
-				Create Target <a href="<%=getDocUrl('replication_doc')%>" target="_blank"><i class="icon-question-sign" rel="tooltip" title="Create the target database"></i></a>
-			</label>
 		</div>
 
-		<button class="btn btn-success btn-large save" type="submit">Replicate</button>
 	</div>
+	<div class="control-group hide" id="step4">
+		<label class="control-label">NAME: </label>
+		<div class="source controls">
+			<p>Give this replication task an ID. (optional)</p>
+			<input type="text" id="repID" name="_id" size="30" value="" data-validation="optional" placeholder="e.g. my_rep">
+
+			<div class="actions">
+				<button class="button green save btn-large fonticon-replicate" type="submit" >Replicate</button>
+			</div>
+		</div>
+
+	</div>
+
+
+
 </form>
 
 <div id="replicationStatus"></div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/62e6dacc/src/fauxton/app/addons/replication/templates/localremotetabs.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/templates/localremotetabs.html b/src/fauxton/app/addons/replication/templates/localremotetabs.html
new file mode 100644
index 0000000..61337e1
--- /dev/null
+++ b/src/fauxton/app/addons/replication/templates/localremotetabs.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.
+-->
+<ul class="nav nav-tabs" id="<%=type%>Tabs">
+  <li class="active">
+  	<a href="#" class="btn local-btn" data-tab="<%=type%>_local">My Databases</a>
+  </li>
+  <li>
+  	<a  href="#" class="btn remote-btn"  data-tab="<%=type%>_remote">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%>" size="30" placeholder="Select a <%=type%> database" class="permission auto">
+		<div class="autharea authArea_<%=type%>"></div>
+	</div>
+
+	<div class="tab-pane" id="<%=type%>_remote">
+		<input type="text" id="to_url" name="<%=type%>" size="30" class="next" value="http://" data-next-step="step<%=step%>">
+		<small>e.g. http://username:password@user.cloudant.com/database</small>
+	</div>
+</div>
+
+<div id="options-here"></div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/62e6dacc/src/fauxton/app/addons/replication/templates/newdatabase.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/templates/newdatabase.html b/src/fauxton/app/addons/replication/templates/newdatabase.html
new file mode 100644
index 0000000..1bcc3bc
--- /dev/null
+++ b/src/fauxton/app/addons/replication/templates/newdatabase.html
@@ -0,0 +1,34 @@
+<!--
+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-tab="<%=type%>_local">Create a new database locally</a>
+  </li>
+  <li>
+  	<a  href="#" class="btn remote-btn"  data-tab="<%=type%>_remote">Create a new 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%>" size="30" placeholder="Name your database" class="permission next" data-next-step="step<%=step%>">
+		<div class="autharea authArea_<%=type%>"></div>
+	</div>
+
+	<div class="tab-pane" id="<%=type%>_remote">
+		<input type="text" id="to_url" name="<%=type%>" size="30" class="next" value="http://" data-next-step="step<%=step%>">
+		<small>e.g. http://username:password@user.cloudant.com/database</small>
+	</div>
+</div>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/62e6dacc/src/fauxton/app/addons/replication/templates/options.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/addons/replication/templates/options.html b/src/fauxton/app/addons/replication/templates/options.html
new file mode 100644
index 0000000..5c6465e
--- /dev/null
+++ b/src/fauxton/app/addons/replication/templates/options.html
@@ -0,0 +1,36 @@
+<!--
+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/62e6dacc/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
index f4b96fd..3366937 100644
--- a/src/fauxton/app/addons/replication/views.js
+++ b/src/fauxton/app/addons/replication/views.js
@@ -24,13 +24,13 @@ function(app, FauxtonAPI, Components, replication) {
     intervalId: null
   };
 
+  app.temphost ="http://deathbear.cloudant.com";
   _.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
@@ -46,76 +46,45 @@ function(app, FauxtonAPI, Components, replication) {
     template: "addons/replication/templates/form",
     events:  {
       "submit #replication": "validate",
-      "click .btn-group .btn": "showFields",
-      "click .swap": "swapFields",
-      "click .options": "toggleAdvancedOptions"
+      "change .next": "nextStep",
+      "change #create_target input[type='radio']": "showTargetForm",
+      "click #create_target label": "createTargetActiveState"
     },
     initialize: function(options){
       this.status = options.status;
       this.selectedDB = options.selectedDB;
       this.newRepModel = new replication.Replicate({});
     },
-    afterRender: function(){
-      this.dbSearchTypeahead = new Components.DbSearchTypeahead({
-        dbLimit: 30,
-        el: "input#to_name"
-      });
-
-      this.dbSearchTypeahead.render();
-
-    },
-
     beforeRender:  function(){
       this.insertView("#replicationStatus", new View.ReplicationList({
         collection: this.status
       }));
+
+      this.insertView("#source_form",new View.LocalRemoteTabs({
+        selectedDB: this.selectedDB ||"",
+        type: "source",
+        step: "2"
+      }));
     },
     cleanup: function(){
       clearInterval(pollingInfo.intervalId);
     },
+    createTargetActiveState: function(e){
+      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:hidden','select:hidden').attr('disabled',true);
-    },
-    showFields: function(e){
-      var $currentTarget = this.$(e.currentTarget),
-      targetVal = $currentTarget.val();
-
-      if (targetVal === "local"){
-        $currentTarget.parents('.form_set').addClass('local');
-      }else{
-        $currentTarget.parents('.form_set').removeClass('local');
-      }
+      this.$el.find('input[type="text"]:hidden','select:hidden').not("[type='radio']").attr('disabled',true);
     },
     establish: function(){
       return [ this.collection.fetch(), this.status.fetch()];
     },
-    validate: function(e){
-      e.preventDefault();
-      var notification;
-      if (this.formValidation()){
-        notification = FauxtonAPI.addNotification({
-          msg: "Please enter every field.",
-          type: "error",
-          clear: true
-        });
-      }else if (this.$('input#to_name').is(':visible') && !this.$('input[name=create_target]').is(':checked')){
-        var alreadyExists = this.collection.where({"name":this.$('input#to_name').val()});
-        if (alreadyExists.length === 0){
-          notification = FauxtonAPI.addNotification({
-            msg: "This database doesn't exist. Check create target if you want to create it.",
-            type: "error",
-            clear: true
-          });
-        }
-      }else{
-        this.submit(e);
-      }
-    },
-    formValidation: function(e){
-      var $remote = this.$el.find('input:visible'),
+    validationCheck: function(e){
+      var $remote = this.$el.find('input:visible').not('[data-validation="optional"]'),
       error = false;
       for(var i=0; i<$remote.length; i++){
         if ($remote[i].value =="http://" || $remote[i].value ===""){
@@ -124,12 +93,24 @@ function(app, FauxtonAPI, Components, replication) {
       }
       return error;
     },
+    nextStep: function(e){
+      this.$("#"+this.$(e.currentTarget).attr('data-next-step')).removeClass('hide');
+    },
     serialize: function(){
       return {
-        databases:  this.collection.toJSON(),
-        selectedDB: this.selectedDB
+        host:  app.host+"/"
       };
     },
+    showTargetForm: function(e){
+      if (this.targetForm){ this.targetForm.remove();}
+      var targetView = this.$('[name="create_target"]:checked').val()==="true"? "CreateTarget": "LocalRemoteTabs";
+      this.targetForm = this.insertView("#target_form",new View[targetView]({
+                          type: "target",
+                          step: "4"
+                        }));
+      this.targetForm.render();
+      this.nextStep(e);
+    },
     startReplication: function(json){
       var that = this;
       this.newRepModel.save(json,{
@@ -153,7 +134,7 @@ function(app, FauxtonAPI, Components, replication) {
         }
       });
       this.enableFields();
-    },		
+    },	
     updateButtonText: function(wait){
       var $button = this.$('#replication button[type=submit]');
       if(wait){
@@ -162,41 +143,241 @@ function(app, FauxtonAPI, Components, replication) {
         $button.text('Replication').attr('disabled', false);
       }
     },
+    validate: function(e){
+      e.preventDefault();
+      var notification;
+
+      if (this.validationCheck()){
+        notification = FauxtonAPI.addNotification({
+          msg: "Please enter every field.",
+          type: "error",
+          clear: true
+        });
+      } else if (this.$('input#to_name').is(':visible') && !this.$('input[name=create_target]').is(':checked')){
+        var alreadyExists = this.collection.where({"name":this.$('input#to_name').val()});
+        if (alreadyExists.length === 0){
+          notification = FauxtonAPI.addNotification({
+            msg: "This database doesn't exist. Select New Database if you want to create it.",
+            type: "error",
+            clear: true
+          });
+        }else{
+          this.submit(e);
+        }
+      }else{
+        this.submit(e);
+      }
+    },
     submit: function(e){
-      this.disableFields(); 
-      var formJSON = {};
+      this.disableFields();
+      var formData = this.scrubFormData(e),
+          that = this;
+      this.updateButtonText(true);
+      if (this.collection.where({"name":"_replicator"}).length !==0){
+        this.startReplication(formData);
+      } else {
+        var db = new this.collection.model();
+        db.save({
+          id: "_replicator",
+          name: "_replicator"
+        }).done(function(){
+          that.startReplication(formData);
+        });
+      }
+     
+    },
+    setAuthHeaders: function(source,user,pass){
+      var basicHeader = new FauxtonAPI.session.createBasicAuthHeader(user,pass),
+          json = {};
+          json.url = app.temphost+"/"+source;
+          json.headers = {
+              "Authorization": basicHeader.basicAuthHeader
+          };
+      return json;
+    },
+    scrubFormData: function(e){
+      var data = {},
+          scrub = {};
       _.map(this.$(e.currentTarget).serializeArray(), function(formData){
         if(formData.value !== ''){
-          formJSON[formData.name] = (formData.value ==="true"? true: formData.value.replace(/\s/g, '').toLowerCase());
+          //clean booleans & whitespaces
+          if (formData.name == "_id" || formData.name == "create_target" ){
+            data[formData.name] = (formData.value ==="true"? true: formData.value.replace(/\s/g, '').toLowerCase());
+          } else {
+          //Lotta stuff needs to be scrubbed before it's in proper json to submit
+            scrub[formData.name] = formData.value.replace(/\s/g, '').toLowerCase();
+          }
         }
       });
 
-      this.updateButtonText(true);
-      this.startReplication(formJSON);
-    },	
-    swapFields: function(e){
+      //username & password for source
+      if ( scrub.user_source && scrub.password_source){
+        data.source = this.setAuthHeaders(scrub.source, scrub.user_source, scrub.password_source);
+      } else {
+        data.source = scrub.source;
+      }
+
+      //username & password for target
+      if ( scrub.user_target && scrub.password_target){
+        data.target = this.setAuthHeaders(scrub.target, scrub.user_target, scrub.password_target);
+      } else {
+        data.target = scrub.target;
+      }
+
+      return data;
+    }
+  });
+
+  View.AdvancedOptions = FauxtonAPI.View.extend({
+    className: "authenticate",
+    template: "addons/replication/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/replication/templates/localremotetabs",
+    events:  {
+      "click .nav-tabs a": "tabs",
+      "change .permission": "showAuth"
+    },
+    afterRender: function(){
+      this.dbSearchTypeahead = new Components.DbSearchTypeahead({
+        dbLimit: 30,
+        el: "input.auto",
+        updater: function(item){
+            return app.host+"/"+item;
+        }
+      });
+      this.dbSearchTypeahead.render();
+
+      this.preselectedDatabase();
+    },
+    initialize: function(options){
+      this.type = options.type;
+      this.step = options.step;
+      this.selected = options.selectedDB || "";
+    },
+    preselectedDatabase: function(){
+      //if selected database is passed through from the _all_dbs page
+      if (this.selected){
+        this.$('input.auto').val(this.selected);
+        this.showAuthFields();
+      }
+    },
+    showAdvancedOptions:  function(e){
+      if (this.advancedOptions){ this.advancedOptions.remove();}
+        this.advancedOptions = this.insertView("#options-here", new View.AdvancedOptions({}));
+        this.advancedOptions.render();
+    },
+    showAuthFields: function(e){
+      var dataAuthSelector = this.$('input.auto').attr('name'),
+          autharea = ".authArea_"+dataAuthSelector,
+          nextStep = this.step;
+
+      if (this[dataAuthSelector]){ this[dataAuthSelector].remove();}
+
+      this[dataAuthSelector] = this.insertView(autharea, new View.AuthFields({
+                                type: dataAuthSelector,
+                                step: nextStep }));
+      this[dataAuthSelector].render();
+    },
+    showAuth: function(e){
+      if (this.$(e.currentTarget).attr('name') === "source"){
+        this.showAdvancedOptions(e);
+      }
+      this.showAuthFields(e);
+    },
+    tabs: function(e){
       e.preventDefault();
-      //WALL O' VARIABLES
-      var $fromSelect = this.$('#from_name'),
-          $toSelect = this.$('#to_name'),
-          $toInput = this.$('#to_url'),
-          $fromInput = this.$('#from_url'),
-          fromSelectVal = $fromSelect.val(),
-          fromInputVal = $fromInput.val(),
-          toSelectVal = $toSelect.val(),
-          toInputVal = $toInput.val();
+      var $currentTarget = this.$(e.currentTarget),
+          getTabID = "#"+$currentTarget.attr('data-tab');
 
-      $fromSelect.val(toSelectVal);
-      $toSelect.val(fromSelectVal);
+      $currentTarget.parents('ul').find('.active').removeClass('active');
+      $currentTarget.parents('li').addClass('active');
 
-      $fromInput.val(toInputVal);
-      $toInput.val(fromInputVal);
+      $(getTabID).parents('.tab-content').find('.active').removeClass('active');
+      $(getTabID).addClass('active');
+    },
+    serialize: function(){
+      return {
+        step: this.step,
+        type: this.type
+      };
     }
   });
 
+  View.CreateTarget = FauxtonAPI.View.extend({
+    template: "addons/replication/templates/newdatabase",
+    events:  {
+      "click .nav-tabs a": "tabs",
+      "change .permission": "showAuth"
+    },
+    initialize: function(options){
+      this.type = options.type;
+      this.step = options.step;
+    },
+    showAuthFields: function(e){
+      var dataAuthSelector = this.$(e.currentTarget).attr('name'),
+          autharea = ".authArea_"+dataAuthSelector;
+
+      if (this[dataAuthSelector]){ this[dataAuthSelector].remove();}
+
+      this[dataAuthSelector] = this.insertView(autharea, new View.AuthFields({
+                                type:dataAuthSelector,
+                                step:"4" 
+                               }));
+      this[dataAuthSelector].render();
+    },
+    showAuth: function(e){
+      if (this.$(e.currentTarget).attr('name') === "source"){
+        this.showAdvancedOptions(e);
+      }
+      this.showAuthFields(e);
+    },
+    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 {
+        step: this.step,
+        type: this.type
+      };
+    }
+  });
+
+
+  View.AuthFields = FauxtonAPI.View.extend({
+    template: "addons/replication/templates/authfields",
+    initialize: function(options){
+      this.type = options.type;
+      this.step = options.step;
+    },
+    serialize: function(){
+      return {
+        step: this.step,
+        type: this.type
+      };
+    }
+  });
 
   View.ReplicationList = FauxtonAPI.View.extend({
     tagName: "ul",
+    className:  "testing",
     initialize:  function(){
       Events.bind('update:tasks', this.establish, this);
       this.listenTo(this.collection, "reset", this.render);

http://git-wip-us.apache.org/repos/asf/couchdb/blob/62e6dacc/src/fauxton/app/templates/layouts/one_pane_notabs.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/layouts/one_pane_notabs.html b/src/fauxton/app/templates/layouts/one_pane_notabs.html
new file mode 100644
index 0000000..f58661f
--- /dev/null
+++ b/src/fauxton/app/templates/layouts/one_pane_notabs.html
@@ -0,0 +1,27 @@
+<!--
+Licensed under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
+-->
+
+<div id="primary-navbar"></div>
+<div id="dashboard" class="container-fluid one-pane">
+  <div class="fixed-header">
+    <div id="breadcrumbs"></div>
+    <div id="api-navbar"></div>
+  </div>
+
+
+  <div class="row-fluid content-area">
+    <div id="dashboard-content" class="window-resizeable"></div>
+  </div>
+</div>
+

http://git-wip-us.apache.org/repos/asf/couchdb/blob/62e6dacc/src/fauxton/assets/js/plugins/happy.js
----------------------------------------------------------------------
diff --git a/src/fauxton/assets/js/plugins/happy.js b/src/fauxton/assets/js/plugins/happy.js
new file mode 100644
index 0000000..5c4dc7c
--- /dev/null
+++ b/src/fauxton/assets/js/plugins/happy.js
@@ -0,0 +1,150 @@
+// 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,
+//       message: 'How are we to reach you sans email??',
+//       test: happy.email // this can be *any* function that returns true or false
+//     }
+//   },
+//   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 fields = [],
+        item;
+    
+    function errorMessaging(error,selector){
+      if(config.errorHandling){
+        config.errorHandling(error);
+      }else{
+        if ($("#"+error.id).length <= 0){
+          var errorEl = $('<span id="'+error.id+'" class="unhappyMessage">'+error.message+'</span>');
+          $(selector).after(errorEl);
+        }
+      }
+    }
+    function handleSubmit(e) {
+      var errors = false, i, l;
+      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 processField(opts, selector) {
+      var field = $(selector),
+          errorM = {
+            message: opts.message,
+            id: field.attr('name') + '_unhappy'
+          };
+        // },
+        // errorEl = $(error.id).length > 0 ? $(error.id) : getError(error);
+        
+      fields.push(field);
+      field.testValid = function (submit) {
+        var val,
+          el = $(this),
+          gotFunc,
+          error = false,
+          temp, 
+          required = !!el.get(0).attributes.getNamedItem('required') || opts.required,
+          password = (field.attr('type') === 'password'),
+          arg = isFunction(opts.arg) ? opts.arg() : opts.arg;
+        
+        // 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
+        gotFunc = ((val.length > 0 || required === 'sometimes') && isFunction(opts.test));
+        
+        // check if we've got an error on our hands
+        if (submit === true && required === true && val.length === 0) {
+          error = true;
+        } else if (gotFunc) {
+          error = !opts.test(val, arg);
+        }
+        
+        if (error) {
+          el.addClass('unhappy');
+          errorMessaging(errorM, el);
+          return false;
+        } else {
+          el.removeClass('unhappy');
+          $("#"+field.attr('name') + '_unhappy').remove();
+          return true;
+        }
+      };
+      field.bind(config.when || 'blur', field.testValid);
+    }
+    
+    for (item in config.fields) {
+      processField(config.fields[item], item);
+    }
+    
+    if (config.submitButton) {
+      $(config.submitButton).click(handleSubmit);
+    } else {
+      this.bind('submit', handleSubmit);
+    }
+    return this;
+  };
+})(this.jQuery || this.Zepto);
+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);
+  }
+};