summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsuelockwood <deathbearbrown@gmail.com>2013-09-26 10:38:10 -0400
committersuelockwood <deathbearbrown@gmail.com>2013-09-30 16:25:46 -0400
commit62e6dacca9629c591316b5790aa8f8c5921870e2 (patch)
tree6cf812679d765ba324bcab2f798b83734989033d
parentb14405041b87867878cd1088bfcca41f3b72f3c6 (diff)
downloadcouchdb-62e6dacca9629c591316b5790aa8f8c5921870e2.tar.gz
New Replication UI and switching to _replicator
-rw-r--r--src/fauxton/app/addons/replication/assets/less/replication.less50
-rw-r--r--src/fauxton/app/addons/replication/resources.js7
-rw-r--r--src/fauxton/app/addons/replication/route.js4
-rw-r--r--src/fauxton/app/addons/replication/templates/authfields.html16
-rw-r--r--src/fauxton/app/addons/replication/templates/form.html87
-rw-r--r--src/fauxton/app/addons/replication/templates/localremotetabs.html35
-rw-r--r--src/fauxton/app/addons/replication/templates/newdatabase.html34
-rw-r--r--src/fauxton/app/addons/replication/templates/options.html36
-rw-r--r--src/fauxton/app/addons/replication/views.js325
-rw-r--r--src/fauxton/app/templates/layouts/one_pane_notabs.html27
-rw-r--r--src/fauxton/assets/js/plugins/happy.js150
11 files changed, 654 insertions, 117 deletions
diff --git a/src/fauxton/app/addons/replication/assets/less/replication.less b/src/fauxton/app/addons/replication/assets/less/replication.less
index a30196654..ea446ec17 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 {
}
}
}
+
diff --git a/src/fauxton/app/addons/replication/resources.js b/src/fauxton/app/addons/replication/resources.js
index 38ae13988..8c916c453 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;
});
diff --git a/src/fauxton/app/addons/replication/route.js b/src/fauxton/app/addons/replication/route.js
index 7ea318c01..0dd25a550 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({});
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 000000000..9806dcfb4
--- /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%>">
diff --git a/src/fauxton/app/addons/replication/templates/form.html b/src/fauxton/app/addons/replication/templates/form.html
index 32a87dca3..e75453efa 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>
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 000000000..61337e13c
--- /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>
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 000000000..1bcc3bc79
--- /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>
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 000000000..5c6465e2d
--- /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>
+
diff --git a/src/fauxton/app/addons/replication/views.js b/src/fauxton/app/addons/replication/views.js
index f4b96fd40..336693748 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);
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 000000000..f58661fce
--- /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>
+
diff --git a/src/fauxton/assets/js/plugins/happy.js b/src/fauxton/assets/js/plugins/happy.js
new file mode 100644
index 000000000..5c4dc7c82
--- /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);
+ }
+};