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/07/23 21:25:07 UTC
[58/64] [abbrv] fauxton commit: updated refs/heads/secondary-indexes
to ef01e24
Pulled views out into their own files
Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/81497990
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/81497990
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/81497990
Branch: refs/heads/secondary-indexes
Commit: 81497990cdec0fbb3d867d3271e416447cbd5fd5
Parents: 4e37185
Author: deathbearbrown <de...@gmail.com>
Authored: Wed Jul 23 09:47:32 2014 -0400
Committer: deathbearbrown <de...@gmail.com>
Committed: Wed Jul 23 09:47:32 2014 -0400
----------------------------------------------------------------------
app/addons/documents/changesviews.js | 84 ++
app/addons/documents/doceditorviews.js | 416 ++++++++
app/addons/documents/indexviews.js | 574 +++++++++++
app/addons/documents/queryoptionviews.js | 270 ++++++
app/addons/documents/routes.js | 27 +-
app/addons/documents/views.js | 1290 +------------------------
6 files changed, 1378 insertions(+), 1283 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/81497990/app/addons/documents/changesviews.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/changesviews.js b/app/addons/documents/changesviews.js
new file mode 100644
index 0000000..2f91c21
--- /dev/null
+++ b/app/addons/documents/changesviews.js
@@ -0,0 +1,84 @@
+// 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",
+ // Libs
+ "addons/fauxton/resizeColumns",
+
+ // Plugins
+ "plugins/prettify",
+ // this should never be global available:
+ // https://github.com/zeroclipboard/zeroclipboard/blob/master/docs/security.md
+ "plugins/zeroclipboard/ZeroClipboard"
+],
+
+function(app, FauxtonAPI, resizeColumns, prettify, ZeroClipboard) {
+
+ var Views = {};
+
+ Views.Changes = FauxtonAPI.View.extend({
+ template: "addons/documents/templates/changes",
+
+ initialize: function () {
+ this.listenTo( this.model.changes, 'sync', this.render);
+ this.listenTo( this.model.changes, 'cachesync', this.render);
+ },
+
+ events: {
+ "click button.js-toggle-json": "toggleJson"
+ },
+
+ toggleJson: function(event) {
+ event.preventDefault();
+
+ var $button = this.$(event.target),
+ $container = $button.closest('.change-box').find(".js-json-container");
+
+ if (!$container.is(":visible")) {
+ $button
+ .text("Close JSON")
+ .addClass("btn-secondary")
+ .removeClass("btn-primary");
+ } else {
+ $button.text("View JSON")
+ .addClass("btn-primary")
+ .removeClass("btn-secondary");
+ }
+
+ $container.slideToggle();
+ },
+
+ establish: function() {
+ return [ this.model.changes.fetchOnce({prefill: true})];
+ },
+
+ serialize: function () {
+ return {
+ changes: this.model.changes.toJSON(),
+ database: this.model
+ };
+ },
+
+ afterRender: function(){
+ prettyPrint();
+ ZeroClipboard.config({ moviePath: "/assets/js/plugins/zeroclipboard/ZeroClipboard.swf" });
+ var client = new ZeroClipboard(this.$(".js-copy"));
+ }
+ });
+
+
+
+ return Views;
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/81497990/app/addons/documents/doceditorviews.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/doceditorviews.js b/app/addons/documents/doceditorviews.js
new file mode 100644
index 0000000..add8d8c
--- /dev/null
+++ b/app/addons/documents/doceditorviews.js
@@ -0,0 +1,416 @@
+// 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/documents/resources",
+ "addons/databases/resources",
+
+ // Libs
+ "addons/fauxton/resizeColumns",
+
+ // Plugins
+ "plugins/prettify"
+],
+
+function(app, FauxtonAPI, Components, Documents, Databases,
+ resizeColumns, prettify) {
+
+ var Views = {};
+
+ /* Attachments upload modal */
+
+ Views.UploadModal = Components.ModalView.extend({
+ template: "addons/documents/templates/upload_modal",
+
+ events: {
+ "click #upload-btn": "uploadFile"
+ },
+
+ uploadFile: function (event) {
+ event.preventDefault();
+
+ var docRev = this.model.get('_rev'),
+ that = this,
+ $form = this.$('#file-upload');
+
+ if (!docRev) {
+ return this.set_error_msg('The document needs to be saved before adding an attachment.');
+ }
+
+ if ($('input[type="file"]')[0].files.length === 0) {
+ return this.set_error_msg('Selected a file to be uploaded.');
+ }
+
+ this.$('#_rev').val(docRev);
+
+ $form.ajaxSubmit({
+ url: this.model.url(),
+ type: 'POST',
+ beforeSend: this.beforeSend,
+ uploadProgress: this.uploadProgress,
+ success: this.success,
+ error: function (resp) {
+ console.log('ERR on upload', resp);
+ return that.set_error_msg('Could not upload document: ' + JSON.parse(resp.responseText).reason);
+ }
+ });
+ },
+
+ success: function (resp) {
+ var hideModal = this.hideModal,
+ $form = this.$('#file-upload');
+
+ FauxtonAPI.triggerRouteEvent('reRenderDoc');
+ //slight delay to make this transistion a little more fluid and less jumpy
+ setTimeout(function () {
+ $form.clearForm();
+ hideModal();
+ $('.modal-backdrop').remove();
+ }, 1000);
+ },
+
+ uploadProgress: function(event, position, total, percentComplete) {
+ this.$('.bar').css({width: percentComplete + '%'});
+ },
+
+ beforeSend: function () {
+ this.$('.progress').removeClass('hide');
+ },
+
+ _showModal: function () {
+ this.$('.bar').css({width: '0%'});
+ this.$('.progress').addClass('hide');
+ }
+ });
+
+
+
+ /* Doc Duplication modal */
+ Views.DuplicateDocModal = Components.ModalView.extend({
+ template: "addons/documents/templates/duplicate_doc_modal",
+
+ initialize: function () {
+ _.bindAll(this);
+ },
+
+ events: {
+ "click #duplicate-btn":"duplicate",
+ "submit #doc-duplicate": "duplicate"
+
+ },
+
+ duplicate: function (event) {
+ event.preventDefault();
+ var newId = this.$('#dup-id').val(),
+ isDDoc = newId.match(/^_design\//),
+ removeDDocID = newId.replace(/^_design\//,""),
+ encodedID = isDDoc? "_design/"+ app.utils.safeURLName(removeDDocID):app.utils.safeURLName(newId);
+
+ this.hideModal();
+ FauxtonAPI.triggerRouteEvent('duplicateDoc', encodedID);
+ },
+
+ _showModal: function () {
+ this.$('.bar').css({width: '0%'});
+ this.$('.progress').addClass('hide');
+ this.clear_error_msg();
+ this.$('.modal').modal();
+ // hack to get modal visible
+ $('.modal-backdrop').css('z-index',1025);
+ },
+
+ showModal: function () {
+ var showModal = this._showModal,
+ setDefaultIdValue = this.setDefaultIdValue,
+ uuid = new FauxtonAPI.UUID();
+
+ uuid.fetch().then(function () {
+ setDefaultIdValue(uuid.next());
+ showModal();
+ });
+ },
+
+ setDefaultIdValue: function (id) {
+ this.$('#dup-id').val(id);
+ }
+ });
+
+ /* Document editor*/
+ Views.CodeEditor = FauxtonAPI.View.extend({
+ template: "addons/documents/templates/code_editor",
+ events: {
+ "click button.save-doc": "saveDoc",
+ "click button.delete": "destroy",
+ "click button.duplicate": "duplicate",
+ "click button.upload": "upload",
+ "click button.cancel-button": "goback"
+ },
+
+ disableLoader: true,
+
+ initialize: function (options) {
+ this.database = options.database;
+ _.bindAll(this);
+ },
+
+ goback: function(){
+ FauxtonAPI.navigate(this.database.url("index") + "?limit=100");
+ },
+
+ destroy: function(event) {
+ if (this.model.isNewDoc()) {
+ FauxtonAPI.addNotification({
+ msg: 'This document has not been saved yet.',
+ type: 'warning',
+ clear: true
+ });
+ return;
+ }
+
+ if (!window.confirm("Are you sure you want to delete this doc?")) {
+ return false;
+ }
+
+ var database = this.model.database;
+
+ this.model.destroy().then(function(resp) {
+ FauxtonAPI.addNotification({
+ msg: "Succesfully deleted your doc",
+ clear: true
+ });
+ FauxtonAPI.navigate(database.url("index"));
+ }, function(resp) {
+ FauxtonAPI.addNotification({
+ msg: "Failed to delete your doc!",
+ type: "error",
+ clear: true
+ });
+ });
+ },
+
+ beforeRender: function () {
+ this.uploadModal = this.setView('#upload-modal', new Views.UploadModal({model: this.model}));
+ this.uploadModal.render();
+
+ this.duplicateModal = this.setView('#duplicate-modal', new Views.DuplicateDocModal({model: this.model}));
+ this.duplicateModal.render();
+ },
+
+ upload: function (event) {
+ event.preventDefault();
+ if (this.model.isNewDoc()) {
+ FauxtonAPI.addNotification({
+ msg: 'Please save the document before uploading an attachment.',
+ type: 'warning',
+ clear: true
+ });
+ return;
+ }
+ this.uploadModal.showModal();
+ },
+
+ duplicate: function(event) {
+ if (this.model.isNewDoc()) {
+ FauxtonAPI.addNotification({
+ msg: 'Please save the document before duplicating it.',
+ type: 'warning',
+ clear: true
+ });
+ return;
+ }
+ event.preventDefault();
+ this.duplicateModal.showModal();
+ },
+
+ updateValues: function() {
+ if (this.model.changedAttributes()) {
+ FauxtonAPI.addNotification({
+ msg: "Document saved successfully.",
+ type: "success",
+ clear: true
+ });
+ this.editor.setValue(this.model.prettyJSON());
+ }
+ },
+
+ establish: function() {
+ var promise = this.model.fetch(),
+ databaseId = this.database.safeID(),
+ deferred = $.Deferred(),
+ that = this;
+
+ promise.then(function () {
+ deferred.resolve();
+ }, function (xhr, reason, msg) {
+ if (xhr.status === 404) {
+ FauxtonAPI.addNotification({
+ msg: 'The document does not exist',
+ type: 'error',
+ clear: true
+ });
+ that.goback();
+ }
+ deferred.reject();
+ });
+
+ return deferred;
+ },
+
+ saveDoc: function(event) {
+ var json,
+ that = this,
+ editor = this.editor,
+ validDoc = this.getDocFromEditor();
+
+ if (validDoc) {
+ this.getDocFromEditor();
+
+ FauxtonAPI.addNotification({msg: "Saving document."});
+
+ this.model.save().then(function () {
+ editor.editSaved();
+ FauxtonAPI.navigate('/database/' + that.database.safeID() + '/' + that.model.id);
+ }).fail(function(xhr) {
+ var responseText = JSON.parse(xhr.responseText).reason;
+ FauxtonAPI.addNotification({
+ msg: "Save failed: " + responseText,
+ type: "error",
+ fade: false,
+ clear: true,
+ selector: "#doc .errors-container"
+ });
+ });
+ } else if(this.model.validationError && this.model.validationError === 'Cannot change a documents id.') {
+ FauxtonAPI.addNotification({
+ msg: "Cannot save: " + 'Cannot change a documents _id, try Duplicate doc instead!',
+ type: "error",
+ selector: "#doc .errors-container",
+ clear: true
+ });
+ delete this.model.validationError;
+ } else {
+ FauxtonAPI.addNotification({
+ msg: "Please fix the JSON errors and try again.",
+ type: "error",
+ selector: "#doc .errors-container",
+ clear: true
+ });
+ }
+ },
+
+ getDocFromEditor: function () {
+ var json;
+
+ if (!this.hasValidCode()) {
+ return false;
+ }
+
+ json = JSON.parse(this.editor.getValue());
+
+ this.model.clear().set(json, {validate: true});
+ if (this.model.validationError) {
+ return false;
+ }
+
+ return this.model;
+ },
+
+ hasValidCode: function() {
+ var errors = this.editor.getAnnotations();
+ return errors.length === 0;
+ },
+
+ serialize: function() {
+ return {
+ doc: this.model,
+ attachments: this.getAttachments()
+ };
+ },
+
+ getAttachments: function () {
+ var attachments = this.model.get('_attachments');
+
+ if (!attachments) { return false; }
+
+ return _.map(attachments, function (att, key) {
+ return {
+ fileName: key,
+ size: att.length,
+ contentType: att.content_type,
+ url: this.model.url() + '/' + app.utils.safeURLName(key)
+ };
+ }, this);
+ },
+
+ afterRender: function() {
+ var saveDoc = this.saveDoc,
+ editor,
+ model;
+
+ this.editor = new Components.Editor({
+ editorId: "editor-container",
+ forceMissingId: true,
+ commands: [{
+ name: 'save',
+ bindKey: {win: 'Ctrl-S', mac: 'Ctrl-S'},
+ exec: function(editor) {
+ saveDoc();
+ },
+ readOnly: true // false if this command should not apply in readOnly mode
+ }]
+ });
+ this.editor.render();
+
+ editor = this.editor;
+ model = this.model;
+
+ this.listenTo(this.model, "sync", this.updateValues);
+ this.listenTo(editor.editor, "change", function (event) {
+ var changedDoc;
+ try {
+ changedDoc = JSON.parse(editor.getValue());
+ } catch(exception) {
+ //not complete doc. Cannot work with it
+ return;
+ }
+
+ var keyChecked = ["_id"];
+ if (model.get("_rev")) { keyChecked.push("_rev");}
+
+ //check the changedDoc has all the required standard keys
+ if (_.isEmpty(_.difference(keyChecked, _.keys(changedDoc)))) { return; }
+
+ editor.setReadOnly(true);
+ setTimeout(function () { editor.setReadOnly(false);}, 400);
+ // use extend so that _id stays at the top of the object with displaying the doc
+ changedDoc = _.extend({_id: model.id, _rev: model.get("_rev")}, changedDoc);
+ editor.setValue(JSON.stringify(changedDoc, null, " "));
+ FauxtonAPI.addNotification({
+ type: "error",
+ msg: "Cannot remove a documents Id or Revision.",
+ clear: true
+ });
+ });
+ },
+
+ cleanup: function () {
+ if (this.editor) this.editor.remove();
+ }
+ });
+
+ return Views;
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/81497990/app/addons/documents/indexviews.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/indexviews.js b/app/addons/documents/indexviews.js
new file mode 100644
index 0000000..df8c10d
--- /dev/null
+++ b/app/addons/documents/indexviews.js
@@ -0,0 +1,574 @@
+// 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/documents/resources",
+ "addons/databases/resources",
+ "addons/pouchdb/base",
+ //views
+ "addons/documents/queryoptionviews",
+ // Libs
+ "addons/fauxton/resizeColumns",
+
+ // Plugins
+ "plugins/beautify",
+ "plugins/prettify"
+],
+
+function(app, FauxtonAPI, Components, Documents, Databases, pouchdb,
+ QueryOptions, resizeColumns, beautify, prettify) {
+
+ var Views = {};
+
+ Views.ViewEditor = FauxtonAPI.View.extend({
+ template: "addons/documents/templates/view_editor",
+ builtinReduces: ['_sum', '_count', '_stats'],
+
+ events: {
+ "click button.save": "saveView",
+ "click button.delete": "deleteView",
+ "change select#reduce-function-selector": "updateReduce",
+ "click button.preview": "previewView",
+ "click #db-views-tabs-nav": 'toggleIndexNav',
+ "click .beautify_map": "beautifyCode",
+ "click .beautify_reduce": "beautifyCode",
+ "click #query-options-wrapper": 'toggleIndexNav'
+ },
+
+ langTemplates: {
+ "javascript": {
+ map: "function(doc) {\n emit(doc._id, 1);\n}",
+ reduce: "function(keys, values, rereduce){\n if (rereduce){\n return sum(values);\n } else {\n return values.length;\n }\n}"
+ }
+ },
+
+ defaultLang: "javascript",
+
+ initialize: function(options) {
+ this.newView = options.newView || false;
+ this.ddocs = options.ddocs;
+ this.params = options.params;
+ this.database = options.database;
+ this.currentDdoc = options.currentddoc;
+ if (this.newView) {
+ this.viewName = 'newView';
+ } else {
+ this.ddocID = options.ddocInfo.id;
+ this.viewName = options.viewName;
+ this.ddocInfo = new Documents.DdocInfo({_id: this.ddocID},{database: this.database});
+ }
+
+ this.showIndex = false;
+ _.bindAll(this);
+ },
+
+ establish: function () {
+ if (this.ddocInfo) {
+ return this.ddocInfo.fetch();
+ }
+ },
+
+ updateValues: function() {
+ var notification;
+ if (this.model.changedAttributes()) {
+ notification = FauxtonAPI.addNotification({
+ msg: "Document saved successfully.",
+ type: "success",
+ clear: true
+ });
+ this.editor.setValue(this.model.prettyJSON());
+ }
+ },
+
+ updateReduce: function(event) {
+ var $ele = $("#reduce-function-selector");
+ var $reduceContainer = $(".control-group.reduce-function");
+ if ($ele.val() == "CUSTOM") {
+ this.createReduceEditor();
+ this.reduceEditor.setValue(this.langTemplates.javascript.reduce);
+ $reduceContainer.show();
+ } else {
+ $reduceContainer.hide();
+ }
+ },
+
+ deleteView: function (event) {
+ event.preventDefault();
+
+ if (this.newView) { return alert('Cannot delete a new view.'); }
+ if (!confirm('Are you sure you want to delete this view?')) {return;}
+
+ var that = this,
+ promise,
+ viewName = this.$('#index-name').val(),
+ ddocName = this.$('#ddoc :selected').val(),
+ ddoc = this.getCurrentDesignDoc();
+
+ ddoc.removeDdocView(viewName);
+
+ if (ddoc.hasViews()) {
+ promise = ddoc.save();
+ } else {
+ promise = ddoc.destroy();
+ }
+
+ promise.then(function () {
+ FauxtonAPI.navigate('/database/' + that.database.safeID() + '/_all_docs?limit=' + Databases.DocLimit);
+ FauxtonAPI.triggerRouteEvent('reloadDesignDocs');
+ });
+ },
+
+ saveView: function(event) {
+ var json, notification,
+ that = this;
+
+ if (event) { event.preventDefault();}
+
+ $('#dashboard-content').scrollTop(0); //scroll up
+
+ if (this.hasValidCode() && this.$('#new-ddoc:visible').val() !=="") {
+ var mapVal = this.mapEditor.getValue(),
+ reduceVal = this.reduceVal(),
+ viewName = this.$('#index-name').val(),
+ ddoc = this.getCurrentDesignDoc(),
+ ddocName = ddoc.id,
+ viewNameChange = false;
+
+ if (this.viewName !== viewName) {
+ ddoc.removeDdocView(this.viewName);
+ this.viewName = viewName;
+ viewNameChange = true;
+ }
+
+ notification = FauxtonAPI.addNotification({
+ msg: "Saving document.",
+ selector: "#define-view .errors-container",
+ clear: true
+ });
+
+ ddoc.setDdocView(viewName, mapVal, reduceVal);
+
+ ddoc.save().then(function () {
+ that.ddocs.add(ddoc);
+
+ that.mapEditor.editSaved();
+ that.reduceEditor && that.reduceEditor.editSaved();
+
+
+ FauxtonAPI.addNotification({
+ msg: "View has been saved.",
+ type: "success",
+ selector: "#define-view .errors-container",
+ clear: true
+ });
+
+ if (that.newView || viewNameChange) {
+ var fragment = '/database/' + that.database.safeID() +'/' + ddoc.safeID() + '/_view/' + app.utils.safeURLName(viewName);
+
+ FauxtonAPI.navigate(fragment, {trigger: false});
+ that.newView = false;
+ that.ddocID = ddoc.safeID();
+ that.viewName = viewName;
+ that.ddocInfo = ddoc;
+ that.showIndex = true;
+ that.render();
+ FauxtonAPI.triggerRouteEvent('reloadDesignDocs', {
+ selectedTab: app.utils.removeSpecialCharacters(ddocName.replace(/_design\//,'')) + '_' + app.utils.removeSpecialCharacters(viewName)
+ });
+ }
+
+ if (that.reduceFunStr !== reduceVal) {
+ that.reduceFunStr = reduceVal;
+ that.advancedOptions.renderOnUpdatehasReduce(that.hasReduce());
+ }
+
+ FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: ddocName, view: viewName});
+
+ }, function(xhr) {
+ var responseText = JSON.parse(xhr.responseText).reason;
+ notification = FauxtonAPI.addNotification({
+ msg: "Save failed: " + responseText,
+ type: "error",
+ clear: true
+ });
+ });
+ } else {
+ var errormessage = (this.$('#new-ddoc:visible').val() ==="")?"Enter a design doc name":"Please fix the Javascript errors and try again.";
+ notification = FauxtonAPI.addNotification({
+ msg: errormessage,
+ type: "error",
+ selector: "#define-view .errors-container",
+ clear: true
+ });
+ }
+ },
+
+ updateView: function(event, paramInfo) {
+ event.preventDefault();
+
+ if (this.newView) { return alert('Please save this new view before querying it.'); }
+
+ var errorParams = paramInfo.errorParams,
+ params = paramInfo.params;
+
+ if (_.any(errorParams)) {
+ _.map(errorParams, function(param) {
+
+ // TODO: Where to add this error?
+ // bootstrap wants the error on a control-group div, but we're not using that
+ //$('form.view-query-update input[name='+param+'], form.view-query-update select[name='+param+']').addClass('error');
+ return FauxtonAPI.addNotification({
+ msg: "JSON Parse Error on field: "+param.name,
+ type: "error",
+ selector: ".advanced-options .errors-container",
+ clear: true
+ });
+ });
+ FauxtonAPI.addNotification({
+ msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
+ type: "warning",
+ selector: ".advanced-options .errors-container",
+ clear: true
+ });
+
+ return false;
+ }
+
+ var fragment = window.location.hash.replace(/\?.*$/, '');
+ if (!_.isEmpty(params)) {
+ fragment = fragment + '?' + $.param(params);
+ }
+
+ FauxtonAPI.navigate(fragment, {trigger: false});
+ FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: this.ddocID, view: this.viewName});
+ },
+
+
+ previewView: function(event, paramsInfo) {
+ event.preventDefault();
+ var that = this,
+ mapVal = this.mapVal(),
+ reduceVal = this.reduceVal(),
+ paramsArr = [];
+
+ if (paramsInfo && paramsInfo.params) {
+ paramsArr = paramsInfo.params;
+ }
+
+ var params = _.reduce(paramsArr, function (params, param) {
+ params[param.name] = param.value;
+ return params;
+ }, {reduce: false});
+
+ FauxtonAPI.addNotification({
+ msg: "<strong>Warning!</strong> Preview executes the Map/Reduce functions in your browser, and may behave differently from CouchDB.",
+ type: "warning",
+ selector: ".advanced-options .errors-container",
+ fade: true,
+ escape: false // beware of possible XSS when the message changes
+ });
+
+ var promise = FauxtonAPI.Deferred();
+
+ if (!this.database.allDocs || this.database.allDocs.params.include_docs !== true) {
+ this.database.buildAllDocs({limit: Databases.DocLimit.toString(), include_docs: true});
+ promise = this.database.allDocs.fetch();
+ } else {
+ promise.resolve();
+ }
+
+ promise.then(function () {
+ params.docs = that.database.allDocs.map(function (model) { return model.get('doc');});
+ var queryPromise = pouchdb.runViewQuery({map: mapVal, reduce: reduceVal}, params);
+ queryPromise.then(function (results) {
+ FauxtonAPI.triggerRouteEvent('updatePreviewDocs', {rows: results.rows, ddoc: that.getCurrentDesignDoc().id, view: that.viewName});
+ });
+ });
+ },
+
+ getCurrentDesignDoc: function () {
+ return this.designDocSelector.getCurrentDesignDoc();
+ },
+
+ isCustomReduceEnabled: function() {
+ return $("#reduce-function-selector").val() == "CUSTOM";
+ },
+
+ mapVal: function () {
+ if (this.mapEditor) {
+ return this.mapEditor.getValue();
+ }
+
+ return this.$('#map-function').text();
+ },
+
+ reduceVal: function() {
+ var reduceOption = this.$('#reduce-function-selector :selected').val(),
+ reduceVal = "";
+
+ if (reduceOption === 'CUSTOM') {
+ if (!this.reduceEditor) { this.createReduceEditor(); }
+ reduceVal = this.reduceEditor.getValue();
+ } else if ( reduceOption !== 'NONE') {
+ reduceVal = reduceOption;
+ }
+
+ return reduceVal;
+ },
+
+
+ hasValidCode: function() {
+ return _.every(["mapEditor", "reduceEditor"], function(editorName) {
+ var editor = this[editorName];
+ if (editorName === "reduceEditor" && ! this.isCustomReduceEnabled()) {
+ return true;
+ }
+ return editor.hadValidCode();
+ }, this);
+ },
+
+ toggleIndexNav: function (event) {
+ $('#dashboard-content').scrollTop(0); //scroll up
+
+ var $targetId = this.$(event.target).attr('id'),
+ $previousTab = this.$(this.$('li.active a').attr('href')),
+ $targetTab = this.$(this.$(event.target).attr('href'));
+
+ if ($targetTab.attr('id') !== $previousTab.attr('id')) {
+ $previousTab.removeAttr('style');
+ }
+
+ if ($targetId === 'index-nav') {
+ if (this.newView) { return; }
+ var that = this;
+ $('#dashboard-content').scrollTop(0); //scroll up
+ $targetTab.toggle('slow', function(){
+ that.showEditors();
+ });
+ } else {
+ $targetTab.toggle('slow');
+ }
+ },
+
+ serialize: function() {
+ return {
+ ddocs: this.ddocs,
+ ddoc: this.model,
+ ddocName: this.model.id,
+ viewName: this.viewName,
+ reduceFunStr: this.reduceFunStr,
+ isCustomReduce: this.hasCustomReduce(),
+ newView: this.newView,
+ langTemplates: this.langTemplates.javascript
+ };
+ },
+
+ hasCustomReduce: function() {
+ return this.reduceFunStr && ! _.contains(this.builtinReduces, this.reduceFunStr);
+ },
+
+ hasReduce: function () {
+ return this.reduceFunStr || false;
+ },
+
+ createReduceEditor: function () {
+ if (this.reduceEditor) {
+ this.reduceEditor.remove();
+ }
+
+ this.reduceEditor = new Components.Editor({
+ editorId: "reduce-function",
+ mode: "javascript",
+ couchJSHINT: true
+ });
+ this.reduceEditor.render();
+
+ if (this.reduceEditor.getLines() === 1){
+ this.$('.beautify_reduce').removeClass("hide");
+ $('.beautify-tooltip').tooltip();
+ }
+ },
+ beforeRender: function () {
+
+ if (this.newView) {
+ this.reduceFunStr = '';
+ if (this.ddocs.length === 0) {
+ this.model = new Documents.Doc(null, {database: this.database});
+ } else {
+ this.model = this.ddocs.first().dDocModel();
+ }
+ this.ddocID = this.model.id;
+ } else {
+ var ddocDecode = decodeURIComponent(this.ddocID);
+ this.model = this.ddocs.get(this.ddocID).dDocModel();
+ this.reduceFunStr = this.model.viewHasReduce(this.viewName);
+ }
+
+ var viewFilters = FauxtonAPI.getExtensions('sidebar:viewFilters'),
+ filteredModels = this.ddocs.models,
+ designDocs = this.ddocs.clone();
+
+ if (!_.isEmpty(viewFilters)) {
+ _.each(viewFilters, function (filter) {
+ filteredModels = _.filter(filteredModels, filter);
+ });
+ designDocs.reset(filteredModels, {silent: true});
+ }
+
+ this.designDocSelector = this.setView('.design-doc-group', new Views.DesignDocSelector({
+ collection: designDocs,
+ ddocName: this.currentDdoc || this.model.id,
+ database: this.database
+ }));
+
+ if (!this.newView) {
+ this.eventer = _.extend({}, Backbone.Events);
+
+ this.advancedOptions = this.insertView('#query', new QueryOptions.AdvancedOptions({
+ updateViewFn: this.updateView,
+ previewFn: this.previewView,
+ database: this.database,
+ viewName: this.viewName,
+ ddocName: this.model.id,
+ hasReduce: this.hasReduce(),
+ eventer: this.eventer,
+ showStale: true
+ }));
+ }
+
+ },
+
+ afterRender: function() {
+
+ if (this.params && !this.newView) {
+ this.advancedOptions.updateFromParams(this.params);
+ }
+
+ this.designDocSelector.updateDesignDoc();
+ if (this.newView || this.showIndex) {
+ this.showEditors();
+ this.showIndex = false;
+ } else {
+ this.$('#index').hide();
+ this.$('#index-nav').parent().removeClass('active');
+ }
+
+ },
+
+ showEditors: function () {
+ this.mapEditor = new Components.Editor({
+ editorId: "map-function",
+ mode: "javascript",
+ couchJSHINT: true
+ });
+ this.mapEditor.render();
+
+ if (this.hasCustomReduce()) {
+ this.createReduceEditor();
+ } else {
+ $(".control-group.reduce-function").hide();
+ }
+
+ if (this.newView) {
+ this.mapEditor.setValue(this.langTemplates[this.defaultLang].map);
+ //Use a built in view by default
+ //this.reduceEditor.setValue(this.langTemplates[this.defaultLang].reduce);
+ }
+
+ this.mapEditor.editSaved();
+ this.reduceEditor && this.reduceEditor.editSaved();
+
+ if (this.mapEditor.getLines() === 1){
+ this.$('.beautify_map').removeClass("hide");
+ $('.beautify-tooltip').tooltip();
+ }
+ },
+ beautifyCode: function(e){
+ e.preventDefault();
+ var targetEditor = $(e.currentTarget).hasClass('beautify_reduce')?this.reduceEditor:this.mapEditor;
+ var beautifiedCode = beautify(targetEditor.getValue());
+ targetEditor.setValue(beautifiedCode);
+ },
+ cleanup: function () {
+ this.mapEditor && this.mapEditor.remove();
+ this.reduceEditor && this.reduceEditor.remove();
+ }
+ });
+
+ Views.DesignDocSelector = FauxtonAPI.View.extend({
+ template: "addons/documents/templates/design_doc_selector",
+
+ events: {
+ "change select#ddoc": "updateDesignDoc"
+ },
+
+ initialize: function (options) {
+ this.ddocName = options.ddocName;
+ this.database = options.database;
+ this.listenTo(this.collection, 'add', this.ddocAdded);
+ this.DocModel = options.DocModel || Documents.Doc;
+ },
+
+ ddocAdded: function (ddoc) {
+ this.ddocName = ddoc.id;
+ this.render();
+ },
+
+ serialize: function () {
+ return {
+ ddocName: this.ddocName,
+ ddocs: this.collection
+ };
+ },
+
+ updateDesignDoc: function () {
+ if (this.newDesignDoc()) {
+ this.$('#new-ddoc-section').show();
+ } else {
+ this.$('#new-ddoc-section').hide();
+ }
+ },
+
+ newDesignDoc: function () {
+
+ return this.$('#ddoc').val() === 'new-doc';
+ },
+
+ newDocValidation: function(){
+ return this.newDesignDoc() && this.$('#new-ddoc').val()==="";
+ },
+ getCurrentDesignDoc: function () {
+ if (this.newDesignDoc()) {
+ var doc = {
+ _id: '_design/' + this.$('#new-ddoc').val(),
+ views: {},
+ language: "javascript"
+ };
+ var ddoc = new this.DocModel(doc, {database: this.database});
+ //this.collection.add(ddoc);
+ return ddoc;
+ } else if ( !this.newDesignDoc() ) {
+ var ddocName = this.$('#ddoc').val();
+ return this.collection.find(function (ddoc) {
+ return ddoc.id === ddocName;
+ }).dDocModel();
+ }
+ }
+ });
+
+ return Views;
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/81497990/app/addons/documents/queryoptionviews.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/queryoptionviews.js b/app/addons/documents/queryoptionviews.js
new file mode 100644
index 0000000..b2e1af5
--- /dev/null
+++ b/app/addons/documents/queryoptionviews.js
@@ -0,0 +1,270 @@
+// 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",
+ // Libs
+ "addons/fauxton/resizeColumns",
+],
+
+function(app, FauxtonAPI, resizeColumns ) {
+
+ var Views = {};
+
+ Views.AdvancedOptions = FauxtonAPI.View.extend({
+ template: "addons/documents/templates/advanced_options",
+ className: "advanced-options well",
+
+ initialize: function (options) {
+ this.database = options.database;
+ this.ddocName = options.ddocName;
+ this.viewName = options.viewName;
+ this.updateViewFn = options.updateViewFn;
+ this.previewFn = options.previewFn;
+ this.showStale = _.isUndefined(options.showStale) ? false : options.showStale;
+ this.hasReduce = _.isUndefined(options.hasReduce) ? true : options.hasReduce;
+ },
+
+ events: {
+ "change form.js-view-query-update input": "updateFilters",
+ "change form.js-view-query-update select": "updateFilters",
+ "submit form.js-view-query-update": "updateView",
+ "click .toggle-btns > label": "toggleQuery"
+ },
+
+ toggleQuery: function(e){
+ e.preventDefault();
+
+ if (this.$(e.currentTarget).hasClass("active")){
+ this.$('.js-query-keys-wrapper').addClass("hide");
+ this.$(".toggle-btns > label").removeClass('active');
+ this.$('.js-query-keys-wrapper').find("input,textarea").attr("disabled","true");
+ } else {
+ this.$('.js-query-keys-wrapper').removeClass("hide");
+ var showFunctionName =this.$(e.currentTarget).attr("for");
+ //highlight current
+ this.$(".toggle-btns > label").removeClass('active');
+ this.$(e.currentTarget).addClass("active");
+ this.$("[id^='js-show']").hide();
+ //show section & disable what needs to be disabled
+ this[showFunctionName]();
+ }
+ },
+
+ showKeys: function(){
+ this.$("#js-showKeys, .js-disabled-message").show();
+ this.$('[name="startkey"],[name="endkey"],[name="inclusive_end"]').attr("disabled","true");
+ this.$('[name="keys"]').removeAttr("disabled");
+ },
+
+ showStartEnd: function(){
+ this.$("#js-showStartEnd").show();
+ this.$('[name="startkey"],[name="endkey"],[name="inclusive_end"]').removeAttr("disabled");
+ this.$('.js-disabled-message').hide();
+ this.$('[name="keys"]').attr("disabled","true");
+ },
+
+ beforeRender: function () {
+ if (this.viewName && this.ddocName) {
+ var buttonViews = FauxtonAPI.getExtensions('advancedOptions:ViewButton');
+ _.each(buttonViews, function (view) {
+ this.insertView('#button-options', view);
+ view.update(this.database, this.ddocName, this.viewName);
+ }, this);
+ }
+ },
+
+ renderOnUpdatehasReduce: function (hasReduce) {
+ this.hasReduce = hasReduce;
+ this.render();
+ },
+
+ parseJSON: function (value) {
+ try {
+ return JSON.parse(value);
+ } catch(e) {
+ return undefined;
+ }
+ },
+
+ validateKeys: function(param){
+ var errorMsg = false,
+ parsedValue = this.parseJSON(param.value);
+
+ if (_.isUndefined(parsedValue)) {
+ errorMsg = "Keys must be valid json.";
+ } else if (!_.isArray(parsedValue)) {
+ errorMsg = "Keys values must be in an array. E.g [1,2,3]";
+ }
+
+ if (errorMsg) {
+ this.$('.js-keys-error').empty();
+ FauxtonAPI.addNotification({
+ type: "error",
+ msg: errorMsg,
+ clear: false,
+ selector: '.advanced-options .errors-container'
+ });
+ return false;
+ }
+
+ return true;
+ },
+ validateFields: function(params){
+ var errors = false;
+ //so ghetto. Spaghetti code.
+ for (var i= 0; i <params.length; i++){
+ if (params[i].name === "skip"){
+ if (!(/^\d+$/).test(params[i].value)){
+ FauxtonAPI.addNotification({
+ msg: "Numbers only for skip",
+ type: "warn",
+ selector: ".advanced-options .errors-container",
+ clear: true
+ });
+ errors = true;
+ }
+ }
+ }
+ return errors;
+ },
+ queryParams: function () {
+ var $form = this.$(".js-view-query-update"),
+ keysParam = false;
+
+ var params = _.reduce($form.serializeArray(), function(params, param) {
+ if (!param.value) { return params; }
+ if (param.name === "limit" && param.value === 'None') { return params; }
+ if (param.name === "keys") { keysParam = param; }
+ params.push(param);
+ return params;
+ }, []);
+
+
+ if (keysParam && !this.validateKeys(keysParam)) { return false; }
+
+ if (params && this.validateFields(params)){ return false; }
+
+ // Validate *key* params to ensure they're valid JSON
+ var keyParams = ["keys","startkey","endkey"];
+ var errorParams = _.filter(params, function(param) {
+ if (_.contains(keyParams, param.name) && _.isUndefined(this.parseJSON(param.value))) {
+ return true;
+ }
+
+ return false;
+ }, this);
+
+ return {params: params, errorParams: errorParams};
+ },
+
+ updateView: function (event) {
+ event.preventDefault();
+ var params = this.queryParams();
+ if (!params) { return;}
+ this.updateViewFn(event, params);
+ },
+
+ updateFilters: function(event) {
+ event.preventDefault();
+ var $ele = $(event.currentTarget);
+ var name = $ele.attr('name');
+ this.updateFiltersFor(name, $ele);
+ },
+
+ updateFiltersFor: function(name, $ele) {
+ var $form = $ele.parents("form.js-view-query-update:first");
+ switch (name) {
+ // Reduce constraints
+ // - Can't include_docs for reduce=true
+ // - can't include group_level for reduce=false
+ case "reduce":
+ if ($ele.prop('checked') === true) {
+ if ($form.find("input[name=include_docs]").prop("checked") === true) {
+ $form.find("input[name=include_docs]").prop("checked", false);
+ var notification = FauxtonAPI.addNotification({
+ msg: "include_docs has been disabled as you cannot include docs on a reduced view",
+ type: "warn",
+ selector: ".advanced-options .errors-container",
+ clear: true
+ });
+ }
+ $form.find("input[name=include_docs]").prop("disabled", true);
+ $form.find("select[name=group_level]").prop("disabled", false);
+ } else {
+ $form.find("select[name=group_level]").val("999").prop("disabled", true);
+ $form.find("input[name=include_docs]").prop("disabled", false);
+ }
+ break;
+ case "skip":
+ if (!(/^\d+$/).test($ele.val())){
+ FauxtonAPI.addNotification({
+ msg: "Numbers only for skip",
+ type: "warn",
+ selector: ".advanced-options .errors-container",
+ clear: true
+ });
+ }
+ break;
+ case "include_docs":
+ break;
+ }
+ },
+
+ updateFromParams: function (params) {
+ var $form = this.$el.find("form.js-view-query-update");
+ _.each(params, function(val, key) {
+ var $ele;
+ switch (key) {
+ case "limit":
+ case "descending":
+ case "group_level":
+ if (!val) { return; }
+ $form.find("select[name='"+key+"']").val(val);
+ break;
+ case "include_docs":
+ case "stale":
+ case "inclusive_end":
+ $form.find("input[name='"+key+"']").prop('checked', true);
+ break;
+ case "reduce":
+ $ele = $form.find("input[name='"+key+"']");
+ if (val == "true") {
+ $ele.prop('checked', true);
+ }
+ this.updateFiltersFor(key, $ele);
+ break;
+ case "key":
+ case "keys":
+ $form.find("textarea[name='"+key+"']").val(val);
+ break;
+ default:
+ $form.find("input[name='"+key+"']").val(val);
+ break;
+ }
+ }, this);
+ },
+
+ serialize: function () {
+ return {
+ hasReduce: this.hasReduce,
+ showPreview: false,
+ showStale: this.showStale
+ };
+ }
+ });
+
+
+ return Views;
+
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/81497990/app/addons/documents/routes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes.js b/app/addons/documents/routes.js
index 276e3fc..2140e04 100644
--- a/app/addons/documents/routes.js
+++ b/app/addons/documents/routes.js
@@ -11,17 +11,22 @@
// the License.
define([
- "app",
+ "app",
- "api",
+ "api",
- // Modules
- "addons/documents/views",
- "addons/databases/base",
- "addons/documents/resources"
+ // Modules
+ //views
+ "addons/documents/views",
+ "addons/documents/changesviews",
+ "addons/documents/indexviews",
+ "addons/documents/doceditorviews",
+
+ "addons/databases/base",
+ "addons/documents/resources"
],
-function(app, FauxtonAPI, Documents, Databases, Resources) {
+function(app, FauxtonAPI, Documents, Changes, Index, DocEditor, Databases, Resources) {
var DocEditorRouteObject = FauxtonAPI.RouteObject.extend({
layout: "one_pane",
@@ -58,7 +63,7 @@ function(app, FauxtonAPI, Documents, Databases, Resources) {
code_editor: function (database, doc) {
- this.docView = this.setView("#dashboard-content", new Documents.Views.CodeEditor({
+ this.docView = this.setView("#dashboard-content", new DocEditor.CodeEditor({
model: this.doc,
database: this.database
}));
@@ -299,7 +304,7 @@ function(app, FauxtonAPI, Documents, Databases, Resources) {
}
});
- this.viewEditor = this.setView("#dashboard-upper-content", new Documents.Views.ViewEditor({
+ this.viewEditor = this.setView("#dashboard-upper-content", new Index.ViewEditor({
model: this.data.database,
ddocs: this.data.designDocs,
viewName: view,
@@ -359,7 +364,7 @@ function(app, FauxtonAPI, Documents, Databases, Resources) {
this.toolsView && this.toolsView.remove();
this.documentsView && this.documentsView.remove();
- this.viewEditor = this.setView("#dashboard-upper-content", new Documents.Views.ViewEditor({
+ this.viewEditor = this.setView("#dashboard-upper-content", new Index.ViewEditor({
currentddoc: "_design/"+designDoc || "",
ddocs: this.data.designDocs,
params: params,
@@ -499,7 +504,7 @@ function(app, FauxtonAPI, Documents, Databases, Resources) {
var docParams = app.getParams();
this.data.database.buildChanges(docParams);
- this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.Changes({
+ this.documentsView = this.setView("#dashboard-lower-content", new Changes.Changes({
model: this.data.database
}));
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/81497990/app/addons/documents/views.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/views.js b/app/addons/documents/views.js
index c88ff39..66305ec 100644
--- a/app/addons/documents/views.js
+++ b/app/addons/documents/views.js
@@ -11,29 +11,24 @@
// the License.
define([
- "app",
-
- "api",
- "addons/fauxton/components",
-
- "addons/documents/resources",
- "addons/documents/sidebarviews",
- "addons/databases/resources",
- "addons/pouchdb/base",
-
- // Libs
- "addons/fauxton/resizeColumns",
-
- // Plugins
- "plugins/beautify",
- "plugins/prettify",
- // this should never be global available:
- // https://github.com/zeroclipboard/zeroclipboard/blob/master/docs/security.md
- "plugins/zeroclipboard/ZeroClipboard"
+ "app",
+
+ "api",
+ "addons/fauxton/components",
+ "addons/documents/resources",
+ "addons/databases/resources",
+
+ //Views
+ "addons/documents/sidebarviews",
+ "addons/documents/queryoptionviews",
+ // Libs
+ "addons/fauxton/resizeColumns",
+ //plugins
+ "plugins/prettify"
],
-function(app, FauxtonAPI, Components, Documents, Views, Databases, pouchdb,
- resizeColumns, beautify, prettify, ZeroClipboard) {
+function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions,
+ resizeColumns, prettify) {
function showError (msg) {
FauxtonAPI.addNotification({
@@ -44,56 +39,6 @@ function(app, FauxtonAPI, Components, Documents, Views, Databases, pouchdb,
}
- Views.SearchBox = FauxtonAPI.View.extend({
- template: "addons/documents/templates/search",
- tagName: "form",
- initialize: function(options){
- this.collection = options.collection;
- this.database = options.database;
- },
- afterRender: function(){
- var collection = this.collection;
- var form = this.$el;
- var searchbox = form.find("input#searchbox");
- var database = this.database;
-
- form.submit(function(evt){
- evt.preventDefault();
- var viewname = form.find("input#view").val().split('/');
- var url = "#database/" + database + "/_design/";
- url += viewname[0] + "/_view/" + viewname[1];
- if (searchbox.val() !== ""){
- // TODO: this'll need to work when val() is a number etc.
- url += '?startkey="' + searchbox.val() + '"';
- }
- FauxtonAPI.navigate(url);
- });
-
- searchbox.typeahead({
- source: function(query, process) {
- // TODO: include _all_docs and view keys somehow
- var views = _.map(collection.pluck('doc'), function(d){
- return _.map(_.keys(d.views), function(view){
- return d._id.split('/')[1] + "/" + view;
- });
- });
- return _.flatten(views);
- },
- minLength: 3,
- updater: function(item){
- // TODO: some way to return the original search box
- this.$element.removeClass('span12');
- this.$element.addClass('span6');
- this.$element.attr('placeholder', 'Search by view key');
- $('<span class="add-on span6">' + item +'</span>').insertBefore(this.$element);
- $('<input type="hidden" id="view" value="' + item +'"/>').insertBefore(this.$element);
- // Remove the type ahead for now
- $('.typehead').unbind();
- }
- });
- }
- });
-
Views.DeleteDBModal = Components.ModalView.extend({
template: "addons/documents/templates/delete_database_modal",
@@ -134,118 +79,7 @@ function(app, FauxtonAPI, Components, Documents, Views, Databases, pouchdb,
}
});
- Views.UploadModal = Components.ModalView.extend({
- template: "addons/documents/templates/upload_modal",
- events: {
- "click #upload-btn": "uploadFile"
- },
-
- uploadFile: function (event) {
- event.preventDefault();
-
- var docRev = this.model.get('_rev'),
- that = this,
- $form = this.$('#file-upload');
-
- if (!docRev) {
- return this.set_error_msg('The document needs to be saved before adding an attachment.');
- }
-
- if ($('input[type="file"]')[0].files.length === 0) {
- return this.set_error_msg('Selected a file to be uploaded.');
- }
-
- this.$('#_rev').val(docRev);
-
- $form.ajaxSubmit({
- url: this.model.url(),
- type: 'POST',
- beforeSend: this.beforeSend,
- uploadProgress: this.uploadProgress,
- success: this.success,
- error: function (resp) {
- console.log('ERR on upload', resp);
- return that.set_error_msg('Could not upload document: ' + JSON.parse(resp.responseText).reason);
- }
- });
- },
-
- success: function (resp) {
- var hideModal = this.hideModal,
- $form = this.$('#file-upload');
-
- FauxtonAPI.triggerRouteEvent('reRenderDoc');
- //slight delay to make this transistion a little more fluid and less jumpy
- setTimeout(function () {
- $form.clearForm();
- hideModal();
- $('.modal-backdrop').remove();
- }, 1000);
- },
-
- uploadProgress: function(event, position, total, percentComplete) {
- this.$('.bar').css({width: percentComplete + '%'});
- },
-
- beforeSend: function () {
- this.$('.progress').removeClass('hide');
- },
-
- _showModal: function () {
- this.$('.bar').css({width: '0%'});
- this.$('.progress').addClass('hide');
- }
- });
-
- Views.DuplicateDocModal = Components.ModalView.extend({
- template: "addons/documents/templates/duplicate_doc_modal",
-
- initialize: function () {
- _.bindAll(this);
- },
-
- events: {
- "click #duplicate-btn":"duplicate",
- "submit #doc-duplicate": "duplicate"
-
- },
-
- duplicate: function (event) {
- event.preventDefault();
- var newId = this.$('#dup-id').val(),
- isDDoc = newId.match(/^_design\//),
- removeDDocID = newId.replace(/^_design\//,""),
- encodedID = isDDoc? "_design/"+ app.utils.safeURLName(removeDDocID):app.utils.safeURLName(newId);
-
- this.hideModal();
- FauxtonAPI.triggerRouteEvent('duplicateDoc', encodedID);
- },
-
- _showModal: function () {
- this.$('.bar').css({width: '0%'});
- this.$('.progress').addClass('hide');
- this.clear_error_msg();
- this.$('.modal').modal();
- // hack to get modal visible
- $('.modal-backdrop').css('z-index',1025);
- },
-
- showModal: function () {
- var showModal = this._showModal,
- setDefaultIdValue = this.setDefaultIdValue,
- uuid = new FauxtonAPI.UUID();
-
- uuid.fetch().then(function () {
- setDefaultIdValue(uuid.next());
- showModal();
- });
- },
-
- setDefaultIdValue: function (id) {
- this.$('#dup-id').val(id);
- }
- });
Views.Document = FauxtonAPI.View.extend({
template: "addons/documents/templates/all_docs_item",
@@ -336,7 +170,7 @@ function(app, FauxtonAPI, Components, Documents, Views, Databases, pouchdb,
}
});
-
+
Views.AllDocsNumber = FauxtonAPI.View.extend({
template: "addons/documents/templates/all_docs_number",
@@ -416,7 +250,7 @@ function(app, FauxtonAPI, Components, Documents, Views, Databases, pouchdb,
},
beforeRender: function () {
- this.advancedOptions = this.insertView('#query', new Views.AdvancedOptions({
+ this.advancedOptions = this.insertView('#query', new QueryOptions.AdvancedOptions({
updateViewFn: this.updateAllDocs,
previewFn: this.previewView,
hasReduce: false,
@@ -717,1046 +551,7 @@ function(app, FauxtonAPI, Components, Documents, Views, Databases, pouchdb,
}
});
- Views.CodeEditor = FauxtonAPI.View.extend({
- template: "addons/documents/templates/code_editor",
- events: {
- "click button.save-doc": "saveDoc",
- "click button.delete": "destroy",
- "click button.duplicate": "duplicate",
- "click button.upload": "upload",
- "click button.cancel-button": "goback"
- },
-
- disableLoader: true,
-
- initialize: function (options) {
- this.database = options.database;
- _.bindAll(this);
- },
-
- goback: function(){
- FauxtonAPI.navigate(this.database.url("index") + "?limit=100");
- },
-
- destroy: function(event) {
- if (this.model.isNewDoc()) {
- FauxtonAPI.addNotification({
- msg: 'This document has not been saved yet.',
- type: 'warning',
- clear: true
- });
- return;
- }
-
- if (!window.confirm("Are you sure you want to delete this doc?")) {
- return false;
- }
-
- var database = this.model.database;
-
- this.model.destroy().then(function(resp) {
- FauxtonAPI.addNotification({
- msg: "Succesfully deleted your doc",
- clear: true
- });
- FauxtonAPI.navigate(database.url("index"));
- }, function(resp) {
- FauxtonAPI.addNotification({
- msg: "Failed to delete your doc!",
- type: "error",
- clear: true
- });
- });
- },
-
- beforeRender: function () {
- this.uploadModal = this.setView('#upload-modal', new Views.UploadModal({model: this.model}));
- this.uploadModal.render();
-
- this.duplicateModal = this.setView('#duplicate-modal', new Views.DuplicateDocModal({model: this.model}));
- this.duplicateModal.render();
- },
-
- upload: function (event) {
- event.preventDefault();
- if (this.model.isNewDoc()) {
- FauxtonAPI.addNotification({
- msg: 'Please save the document before uploading an attachment.',
- type: 'warning',
- clear: true
- });
- return;
- }
- this.uploadModal.showModal();
- },
-
- duplicate: function(event) {
- if (this.model.isNewDoc()) {
- FauxtonAPI.addNotification({
- msg: 'Please save the document before duplicating it.',
- type: 'warning',
- clear: true
- });
- return;
- }
- event.preventDefault();
- this.duplicateModal.showModal();
- },
-
- updateValues: function() {
- if (this.model.changedAttributes()) {
- FauxtonAPI.addNotification({
- msg: "Document saved successfully.",
- type: "success",
- clear: true
- });
- this.editor.setValue(this.model.prettyJSON());
- }
- },
-
- establish: function() {
- var promise = this.model.fetch(),
- databaseId = this.database.safeID(),
- deferred = $.Deferred(),
- that = this;
-
- promise.then(function () {
- deferred.resolve();
- }, function (xhr, reason, msg) {
- if (xhr.status === 404) {
- FauxtonAPI.addNotification({
- msg: 'The document does not exist',
- type: 'error',
- clear: true
- });
- that.goback();
- }
- deferred.reject();
- });
-
- return deferred;
- },
-
- saveDoc: function(event) {
- var json,
- that = this,
- editor = this.editor,
- validDoc = this.getDocFromEditor();
-
- if (validDoc) {
- this.getDocFromEditor();
-
- FauxtonAPI.addNotification({msg: "Saving document."});
-
- this.model.save().then(function () {
- editor.editSaved();
- FauxtonAPI.navigate('/database/' + that.database.safeID() + '/' + that.model.id);
- }).fail(function(xhr) {
- var responseText = JSON.parse(xhr.responseText).reason;
- FauxtonAPI.addNotification({
- msg: "Save failed: " + responseText,
- type: "error",
- fade: false,
- clear: true,
- selector: "#doc .errors-container"
- });
- });
- } else if(this.model.validationError && this.model.validationError === 'Cannot change a documents id.') {
- FauxtonAPI.addNotification({
- msg: "Cannot save: " + 'Cannot change a documents _id, try Duplicate doc instead!',
- type: "error",
- selector: "#doc .errors-container",
- clear: true
- });
- delete this.model.validationError;
- } else {
- FauxtonAPI.addNotification({
- msg: "Please fix the JSON errors and try again.",
- type: "error",
- selector: "#doc .errors-container",
- clear: true
- });
- }
- },
-
- getDocFromEditor: function () {
- var json;
-
- if (!this.hasValidCode()) {
- return false;
- }
-
- json = JSON.parse(this.editor.getValue());
-
- this.model.clear().set(json, {validate: true});
- if (this.model.validationError) {
- return false;
- }
-
- return this.model;
- },
-
- hasValidCode: function() {
- var errors = this.editor.getAnnotations();
- return errors.length === 0;
- },
-
- serialize: function() {
- return {
- doc: this.model,
- attachments: this.getAttachments()
- };
- },
-
- getAttachments: function () {
- var attachments = this.model.get('_attachments');
-
- if (!attachments) { return false; }
-
- return _.map(attachments, function (att, key) {
- return {
- fileName: key,
- size: att.length,
- contentType: att.content_type,
- url: this.model.url() + '/' + app.utils.safeURLName(key)
- };
- }, this);
- },
-
- afterRender: function() {
- var saveDoc = this.saveDoc,
- editor,
- model;
-
- this.editor = new Components.Editor({
- editorId: "editor-container",
- forceMissingId: true,
- commands: [{
- name: 'save',
- bindKey: {win: 'Ctrl-S', mac: 'Ctrl-S'},
- exec: function(editor) {
- saveDoc();
- },
- readOnly: true // false if this command should not apply in readOnly mode
- }]
- });
- this.editor.render();
-
- editor = this.editor;
- model = this.model;
-
- this.listenTo(this.model, "sync", this.updateValues);
- this.listenTo(editor.editor, "change", function (event) {
- var changedDoc;
- try {
- changedDoc = JSON.parse(editor.getValue());
- } catch(exception) {
- //not complete doc. Cannot work with it
- return;
- }
-
- var keyChecked = ["_id"];
- if (model.get("_rev")) { keyChecked.push("_rev");}
-
- //check the changedDoc has all the required standard keys
- if (_.isEmpty(_.difference(keyChecked, _.keys(changedDoc)))) { return; }
-
- editor.setReadOnly(true);
- setTimeout(function () { editor.setReadOnly(false);}, 400);
- // use extend so that _id stays at the top of the object with displaying the doc
- changedDoc = _.extend({_id: model.id, _rev: model.get("_rev")}, changedDoc);
- editor.setValue(JSON.stringify(changedDoc, null, " "));
- FauxtonAPI.addNotification({
- type: "error",
- msg: "Cannot remove a documents Id or Revision.",
- clear: true
- });
- });
- },
-
- cleanup: function () {
- if (this.editor) this.editor.remove();
- }
- });
-
- Views.AdvancedOptions = FauxtonAPI.View.extend({
- template: "addons/documents/templates/advanced_options",
- className: "advanced-options well",
-
- initialize: function (options) {
- this.database = options.database;
- this.ddocName = options.ddocName;
- this.viewName = options.viewName;
- this.updateViewFn = options.updateViewFn;
- this.previewFn = options.previewFn;
- this.showStale = _.isUndefined(options.showStale) ? false : options.showStale;
- this.hasReduce = _.isUndefined(options.hasReduce) ? true : options.hasReduce;
- },
-
- events: {
- "change form.js-view-query-update input": "updateFilters",
- "change form.js-view-query-update select": "updateFilters",
- "submit form.js-view-query-update": "updateView",
- "click .toggle-btns > label": "toggleQuery"
- },
-
- toggleQuery: function(e){
- e.preventDefault();
-
- if (this.$(e.currentTarget).hasClass("active")){
- this.$('.js-query-keys-wrapper').addClass("hide");
- this.$(".toggle-btns > label").removeClass('active');
- this.$('.js-query-keys-wrapper').find("input,textarea").attr("disabled","true");
- } else {
- this.$('.js-query-keys-wrapper').removeClass("hide");
- var showFunctionName =this.$(e.currentTarget).attr("for");
- //highlight current
- this.$(".toggle-btns > label").removeClass('active');
- this.$(e.currentTarget).addClass("active");
- this.$("[id^='js-show']").hide();
- //show section & disable what needs to be disabled
- this[showFunctionName]();
- }
- },
-
- showKeys: function(){
- this.$("#js-showKeys, .js-disabled-message").show();
- this.$('[name="startkey"],[name="endkey"],[name="inclusive_end"]').attr("disabled","true");
- this.$('[name="keys"]').removeAttr("disabled");
- },
-
- showStartEnd: function(){
- this.$("#js-showStartEnd").show();
- this.$('[name="startkey"],[name="endkey"],[name="inclusive_end"]').removeAttr("disabled");
- this.$('.js-disabled-message').hide();
- this.$('[name="keys"]').attr("disabled","true");
- },
-
- beforeRender: function () {
- if (this.viewName && this.ddocName) {
- var buttonViews = FauxtonAPI.getExtensions('advancedOptions:ViewButton');
- _.each(buttonViews, function (view) {
- this.insertView('#button-options', view);
- view.update(this.database, this.ddocName, this.viewName);
- }, this);
- }
- },
-
- renderOnUpdatehasReduce: function (hasReduce) {
- this.hasReduce = hasReduce;
- this.render();
- },
-
- parseJSON: function (value) {
- try {
- return JSON.parse(value);
- } catch(e) {
- return undefined;
- }
- },
-
- validateKeys: function(param){
- var errorMsg = false,
- parsedValue = this.parseJSON(param.value);
-
- if (_.isUndefined(parsedValue)) {
- errorMsg = "Keys must be valid json.";
- } else if (!_.isArray(parsedValue)) {
- errorMsg = "Keys values must be in an array. E.g [1,2,3]";
- }
-
- if (errorMsg) {
- this.$('.js-keys-error').empty();
- FauxtonAPI.addNotification({
- type: "error",
- msg: errorMsg,
- clear: false,
- selector: '.advanced-options .errors-container'
- });
- return false;
- }
-
- return true;
- },
- validateFields: function(params){
- var errors = false;
- //so ghetto. Spaghetti code.
- for (var i= 0; i <params.length; i++){
- if (params[i].name === "skip"){
- if (!(/^\d+$/).test(params[i].value)){
- FauxtonAPI.addNotification({
- msg: "Numbers only for skip",
- type: "warn",
- selector: ".advanced-options .errors-container",
- clear: true
- });
- errors = true;
- }
- }
- }
- return errors;
- },
- queryParams: function () {
- var $form = this.$(".js-view-query-update"),
- keysParam = false;
-
- var params = _.reduce($form.serializeArray(), function(params, param) {
- if (!param.value) { return params; }
- if (param.name === "limit" && param.value === 'None') { return params; }
- if (param.name === "keys") { keysParam = param; }
- params.push(param);
- return params;
- }, []);
-
-
- if (keysParam && !this.validateKeys(keysParam)) { return false; }
-
- if (params && this.validateFields(params)){ return false; }
-
- // Validate *key* params to ensure they're valid JSON
- var keyParams = ["keys","startkey","endkey"];
- var errorParams = _.filter(params, function(param) {
- if (_.contains(keyParams, param.name) && _.isUndefined(this.parseJSON(param.value))) {
- return true;
- }
-
- return false;
- }, this);
-
- return {params: params, errorParams: errorParams};
- },
-
- updateView: function (event) {
- event.preventDefault();
- var params = this.queryParams();
- if (!params) { return;}
- this.updateViewFn(event, params);
- },
-
- updateFilters: function(event) {
- event.preventDefault();
- var $ele = $(event.currentTarget);
- var name = $ele.attr('name');
- this.updateFiltersFor(name, $ele);
- },
-
- updateFiltersFor: function(name, $ele) {
- var $form = $ele.parents("form.js-view-query-update:first");
- switch (name) {
- // Reduce constraints
- // - Can't include_docs for reduce=true
- // - can't include group_level for reduce=false
- case "reduce":
- if ($ele.prop('checked') === true) {
- if ($form.find("input[name=include_docs]").prop("checked") === true) {
- $form.find("input[name=include_docs]").prop("checked", false);
- var notification = FauxtonAPI.addNotification({
- msg: "include_docs has been disabled as you cannot include docs on a reduced view",
- type: "warn",
- selector: ".advanced-options .errors-container",
- clear: true
- });
- }
- $form.find("input[name=include_docs]").prop("disabled", true);
- $form.find("select[name=group_level]").prop("disabled", false);
- } else {
- $form.find("select[name=group_level]").val("999").prop("disabled", true);
- $form.find("input[name=include_docs]").prop("disabled", false);
- }
- break;
- case "skip":
- if (!(/^\d+$/).test($ele.val())){
- FauxtonAPI.addNotification({
- msg: "Numbers only for skip",
- type: "warn",
- selector: ".advanced-options .errors-container",
- clear: true
- });
- }
- break;
- case "include_docs":
- break;
- }
- },
-
- updateFromParams: function (params) {
- var $form = this.$el.find("form.js-view-query-update");
- _.each(params, function(val, key) {
- var $ele;
- switch (key) {
- case "limit":
- case "descending":
- case "group_level":
- if (!val) { return; }
- $form.find("select[name='"+key+"']").val(val);
- break;
- case "include_docs":
- case "stale":
- case "inclusive_end":
- $form.find("input[name='"+key+"']").prop('checked', true);
- break;
- case "reduce":
- $ele = $form.find("input[name='"+key+"']");
- if (val == "true") {
- $ele.prop('checked', true);
- }
- this.updateFiltersFor(key, $ele);
- break;
- case "key":
- case "keys":
- $form.find("textarea[name='"+key+"']").val(val);
- break;
- default:
- $form.find("input[name='"+key+"']").val(val);
- break;
- }
- }, this);
- },
-
- serialize: function () {
- return {
- hasReduce: this.hasReduce,
- showPreview: false,
- showStale: this.showStale
- };
- }
- });
-
- Views.DesignDocSelector = FauxtonAPI.View.extend({
- template: "addons/documents/templates/design_doc_selector",
-
- events: {
- "change select#ddoc": "updateDesignDoc"
- },
-
- initialize: function (options) {
- this.ddocName = options.ddocName;
- this.database = options.database;
- this.listenTo(this.collection, 'add', this.ddocAdded);
- this.DocModel = options.DocModel || Documents.Doc;
- },
-
- ddocAdded: function (ddoc) {
- this.ddocName = ddoc.id;
- this.render();
- },
-
- serialize: function () {
- return {
- ddocName: this.ddocName,
- ddocs: this.collection
- };
- },
-
- updateDesignDoc: function () {
- if (this.newDesignDoc()) {
- this.$('#new-ddoc-section').show();
- } else {
- this.$('#new-ddoc-section').hide();
- }
- },
-
- newDesignDoc: function () {
-
- return this.$('#ddoc').val() === 'new-doc';
- },
-
- newDocValidation: function(){
- return this.newDesignDoc() && this.$('#new-ddoc').val()==="";
- },
- getCurrentDesignDoc: function () {
- if (this.newDesignDoc()) {
- var doc = {
- _id: '_design/' + this.$('#new-ddoc').val(),
- views: {},
- language: "javascript"
- };
- var ddoc = new this.DocModel(doc, {database: this.database});
- //this.collection.add(ddoc);
- return ddoc;
- } else if ( !this.newDesignDoc() ) {
- var ddocName = this.$('#ddoc').val();
- return this.collection.find(function (ddoc) {
- return ddoc.id === ddocName;
- }).dDocModel();
- }
- }
- });
-
- Views.ViewEditor = FauxtonAPI.View.extend({
- template: "addons/documents/templates/view_editor",
- builtinReduces: ['_sum', '_count', '_stats'],
-
- events: {
- "click button.save": "saveView",
- "click button.delete": "deleteView",
- "change select#reduce-function-selector": "updateReduce",
- "click button.preview": "previewView",
- "click #db-views-tabs-nav": 'toggleIndexNav',
- "click .beautify_map": "beautifyCode",
- "click .beautify_reduce": "beautifyCode",
- "click #query-options-wrapper": 'toggleIndexNav'
- },
-
- langTemplates: {
- "javascript": {
- map: "function(doc) {\n emit(doc._id, 1);\n}",
- reduce: "function(keys, values, rereduce){\n if (rereduce){\n return sum(values);\n } else {\n return values.length;\n }\n}"
- }
- },
-
- defaultLang: "javascript",
-
- initialize: function(options) {
- this.newView = options.newView || false;
- this.ddocs = options.ddocs;
- this.params = options.params;
- this.database = options.database;
- this.currentDdoc = options.currentddoc;
- if (this.newView) {
- this.viewName = 'newView';
- } else {
- this.ddocID = options.ddocInfo.id;
- this.viewName = options.viewName;
- this.ddocInfo = new Documents.DdocInfo({_id: this.ddocID},{database: this.database});
- }
-
- this.showIndex = false;
- _.bindAll(this);
- },
-
- establish: function () {
- if (this.ddocInfo) {
- return this.ddocInfo.fetch();
- }
- },
-
- updateValues: function() {
- var notification;
- if (this.model.changedAttributes()) {
- notification = FauxtonAPI.addNotification({
- msg: "Document saved successfully.",
- type: "success",
- clear: true
- });
- this.editor.setValue(this.model.prettyJSON());
- }
- },
-
- updateReduce: function(event) {
- var $ele = $("#reduce-function-selector");
- var $reduceContainer = $(".control-group.reduce-function");
- if ($ele.val() == "CUSTOM") {
- this.createReduceEditor();
- this.reduceEditor.setValue(this.langTemplates.javascript.reduce);
- $reduceContainer.show();
- } else {
- $reduceContainer.hide();
- }
- },
-
- deleteView: function (event) {
- event.preventDefault();
-
- if (this.newView) { return alert('Cannot delete a new view.'); }
- if (!confirm('Are you sure you want to delete this view?')) {return;}
-
- var that = this,
- promise,
- viewName = this.$('#index-name').val(),
- ddocName = this.$('#ddoc :selected').val(),
- ddoc = this.getCurrentDesignDoc();
-
- ddoc.removeDdocView(viewName);
-
- if (ddoc.hasViews()) {
- promise = ddoc.save();
- } else {
- promise = ddoc.destroy();
- }
-
- promise.then(function () {
- FauxtonAPI.navigate('/database/' + that.database.safeID() + '/_all_docs?limit=' + Databases.DocLimit);
- FauxtonAPI.triggerRouteEvent('reloadDesignDocs');
- });
- },
-
- saveView: function(event) {
- var json, notification,
- that = this;
-
- if (event) { event.preventDefault();}
-
- $('#dashboard-content').scrollTop(0); //scroll up
-
- if (this.hasValidCode() && this.$('#new-ddoc:visible').val() !=="") {
- var mapVal = this.mapEditor.getValue(),
- reduceVal = this.reduceVal(),
- viewName = this.$('#index-name').val(),
- ddoc = this.getCurrentDesignDoc(),
- ddocName = ddoc.id,
- viewNameChange = false;
-
- if (this.viewName !== viewName) {
- ddoc.removeDdocView(this.viewName);
- this.viewName = viewName;
- viewNameChange = true;
- }
-
- notification = FauxtonAPI.addNotification({
- msg: "Saving document.",
- selector: "#define-view .errors-container",
- clear: true
- });
-
- ddoc.setDdocView(viewName, mapVal, reduceVal);
-
- ddoc.save().then(function () {
- that.ddocs.add(ddoc);
-
- that.mapEditor.editSaved();
- that.reduceEditor && that.reduceEditor.editSaved();
-
-
- FauxtonAPI.addNotification({
- msg: "View has been saved.",
- type: "success",
- selector: "#define-view .errors-container",
- clear: true
- });
-
- if (that.newView || viewNameChange) {
- var fragment = '/database/' + that.database.safeID() +'/' + ddoc.safeID() + '/_view/' + app.utils.safeURLName(viewName);
-
- FauxtonAPI.navigate(fragment, {trigger: false});
- that.newView = false;
- that.ddocID = ddoc.safeID();
- that.viewName = viewName;
- that.ddocInfo = ddoc;
- that.showIndex = true;
- that.render();
- FauxtonAPI.triggerRouteEvent('reloadDesignDocs', {
- selectedTab: app.utils.removeSpecialCharacters(ddocName.replace(/_design\//,'')) + '_' + app.utils.removeSpecialCharacters(viewName)
- });
- }
-
- if (that.reduceFunStr !== reduceVal) {
- that.reduceFunStr = reduceVal;
- that.advancedOptions.renderOnUpdatehasReduce(that.hasReduce());
- }
-
- FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: ddocName, view: viewName});
-
- }, function(xhr) {
- var responseText = JSON.parse(xhr.responseText).reason;
- notification = FauxtonAPI.addNotification({
- msg: "Save failed: " + responseText,
- type: "error",
- clear: true
- });
- });
- } else {
- var errormessage = (this.$('#new-ddoc:visible').val() ==="")?"Enter a design doc name":"Please fix the Javascript errors and try again.";
- notification = FauxtonAPI.addNotification({
- msg: errormessage,
- type: "error",
- selector: "#define-view .errors-container",
- clear: true
- });
- }
- },
-
- updateView: function(event, paramInfo) {
- event.preventDefault();
-
- if (this.newView) { return alert('Please save this new view before querying it.'); }
-
- var errorParams = paramInfo.errorParams,
- params = paramInfo.params;
-
- if (_.any(errorParams)) {
- _.map(errorParams, function(param) {
-
- // TODO: Where to add this error?
- // bootstrap wants the error on a control-group div, but we're not using that
- //$('form.view-query-update input[name='+param+'], form.view-query-update select[name='+param+']').addClass('error');
- return FauxtonAPI.addNotification({
- msg: "JSON Parse Error on field: "+param.name,
- type: "error",
- selector: ".advanced-options .errors-container",
- clear: true
- });
- });
- FauxtonAPI.addNotification({
- msg: "Make sure that strings are properly quoted and any other values are valid JSON structures",
- type: "warning",
- selector: ".advanced-options .errors-container",
- clear: true
- });
-
- return false;
- }
-
- var fragment = window.location.hash.replace(/\?.*$/, '');
- if (!_.isEmpty(params)) {
- fragment = fragment + '?' + $.param(params);
- }
-
- FauxtonAPI.navigate(fragment, {trigger: false});
- FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: this.ddocID, view: this.viewName});
- },
-
-
- previewView: function(event, paramsInfo) {
- event.preventDefault();
- var that = this,
- mapVal = this.mapVal(),
- reduceVal = this.reduceVal(),
- paramsArr = [];
-
- if (paramsInfo && paramsInfo.params) {
- paramsArr = paramsInfo.params;
- }
-
- var params = _.reduce(paramsArr, function (params, param) {
- params[param.name] = param.value;
- return params;
- }, {reduce: false});
-
- FauxtonAPI.addNotification({
- msg: "<strong>Warning!</strong> Preview executes the Map/Reduce functions in your browser, and may behave differently from CouchDB.",
- type: "warning",
- selector: ".advanced-options .errors-container",
- fade: true,
- escape: false // beware of possible XSS when the message changes
- });
-
- var promise = FauxtonAPI.Deferred();
-
- if (!this.database.allDocs || this.database.allDocs.params.include_docs !== true) {
- this.database.buildAllDocs({limit: Databases.DocLimit.toString(), include_docs: true});
- promise = this.database.allDocs.fetch();
- } else {
- promise.resolve();
- }
-
- promise.then(function () {
- params.docs = that.database.allDocs.map(function (model) { return model.get('doc');});
- var queryPromise = pouchdb.runViewQuery({map: mapVal, reduce: reduceVal}, params);
- queryPromise.then(function (results) {
- FauxtonAPI.triggerRouteEvent('updatePreviewDocs', {rows: results.rows, ddoc: that.getCurrentDesignDoc().id, view: that.viewName});
- });
- });
- },
-
- getCurrentDesignDoc: function () {
- return this.designDocSelector.getCurrentDesignDoc();
- },
-
- isCustomReduceEnabled: function() {
- return $("#reduce-function-selector").val() == "CUSTOM";
- },
-
- mapVal: function () {
- if (this.mapEditor) {
- return this.mapEditor.getValue();
- }
-
- return this.$('#map-function').text();
- },
-
- reduceVal: function() {
- var reduceOption = this.$('#reduce-function-selector :selected').val(),
- reduceVal = "";
-
- if (reduceOption === 'CUSTOM') {
- if (!this.reduceEditor) { this.createReduceEditor(); }
- reduceVal = this.reduceEditor.getValue();
- } else if ( reduceOption !== 'NONE') {
- reduceVal = reduceOption;
- }
-
- return reduceVal;
- },
-
-
- hasValidCode: function() {
- return _.every(["mapEditor", "reduceEditor"], function(editorName) {
- var editor = this[editorName];
- if (editorName === "reduceEditor" && ! this.isCustomReduceEnabled()) {
- return true;
- }
- return editor.hadValidCode();
- }, this);
- },
-
- toggleIndexNav: function (event) {
- $('#dashboard-content').scrollTop(0); //scroll up
-
- var $targetId = this.$(event.target).attr('id'),
- $previousTab = this.$(this.$('li.active a').attr('href')),
- $targetTab = this.$(this.$(event.target).attr('href'));
-
- if ($targetTab.attr('id') !== $previousTab.attr('id')) {
- $previousTab.removeAttr('style');
- }
-
- if ($targetId === 'index-nav') {
- if (this.newView) { return; }
- var that = this;
- $('#dashboard-content').scrollTop(0); //scroll up
- $targetTab.toggle('slow', function(){
- that.showEditors();
- });
- } else {
- $targetTab.toggle('slow');
- }
- },
-
- serialize: function() {
- return {
- ddocs: this.ddocs,
- ddoc: this.model,
- ddocName: this.model.id,
- viewName: this.viewName,
- reduceFunStr: this.reduceFunStr,
- isCustomReduce: this.hasCustomReduce(),
- newView: this.newView,
- langTemplates: this.langTemplates.javascript
- };
- },
-
- hasCustomReduce: function() {
- return this.reduceFunStr && ! _.contains(this.builtinReduces, this.reduceFunStr);
- },
-
- hasReduce: function () {
- return this.reduceFunStr || false;
- },
-
- createReduceEditor: function () {
- if (this.reduceEditor) {
- this.reduceEditor.remove();
- }
-
- this.reduceEditor = new Components.Editor({
- editorId: "reduce-function",
- mode: "javascript",
- couchJSHINT: true
- });
- this.reduceEditor.render();
-
- if (this.reduceEditor.getLines() === 1){
- this.$('.beautify_reduce').removeClass("hide");
- $('.beautify-tooltip').tooltip();
- }
- },
- beforeRender: function () {
-
- if (this.newView) {
- this.reduceFunStr = '';
- if (this.ddocs.length === 0) {
- this.model = new Documents.Doc(null, {database: this.database});
- } else {
- this.model = this.ddocs.first().dDocModel();
- }
- this.ddocID = this.model.id;
- } else {
- var ddocDecode = decodeURIComponent(this.ddocID);
- this.model = this.ddocs.get(this.ddocID).dDocModel();
- this.reduceFunStr = this.model.viewHasReduce(this.viewName);
- }
-
- var viewFilters = FauxtonAPI.getExtensions('sidebar:viewFilters'),
- filteredModels = this.ddocs.models,
- designDocs = this.ddocs.clone();
-
- if (!_.isEmpty(viewFilters)) {
- _.each(viewFilters, function (filter) {
- filteredModels = _.filter(filteredModels, filter);
- });
- designDocs.reset(filteredModels, {silent: true});
- }
-
- this.designDocSelector = this.setView('.design-doc-group', new Views.DesignDocSelector({
- collection: designDocs,
- ddocName: this.currentDdoc || this.model.id,
- database: this.database
- }));
-
- if (!this.newView) {
- this.eventer = _.extend({}, Backbone.Events);
-
- this.advancedOptions = this.insertView('#query', new Views.AdvancedOptions({
- updateViewFn: this.updateView,
- previewFn: this.previewView,
- database: this.database,
- viewName: this.viewName,
- ddocName: this.model.id,
- hasReduce: this.hasReduce(),
- eventer: this.eventer,
- showStale: true
- }));
- }
-
- },
-
- afterRender: function() {
- if (this.params && !this.newView) {
- this.advancedOptions.updateFromParams(this.params);
- }
-
- this.designDocSelector.updateDesignDoc();
- if (this.newView || this.showIndex) {
- this.showEditors();
- this.showIndex = false;
- } else {
- this.$('#index').hide();
- this.$('#index-nav').parent().removeClass('active');
- }
-
- },
-
- showEditors: function () {
- this.mapEditor = new Components.Editor({
- editorId: "map-function",
- mode: "javascript",
- couchJSHINT: true
- });
- this.mapEditor.render();
-
- if (this.hasCustomReduce()) {
- this.createReduceEditor();
- } else {
- $(".control-group.reduce-function").hide();
- }
-
- if (this.newView) {
- this.mapEditor.setValue(this.langTemplates[this.defaultLang].map);
- //Use a built in view by default
- //this.reduceEditor.setValue(this.langTemplates[this.defaultLang].reduce);
- }
-
- this.mapEditor.editSaved();
- this.reduceEditor && this.reduceEditor.editSaved();
-
- if (this.mapEditor.getLines() === 1){
- this.$('.beautify_map').removeClass("hide");
- $('.beautify-tooltip').tooltip();
- }
- },
- beautifyCode: function(e){
- e.preventDefault();
- var targetEditor = $(e.currentTarget).hasClass('beautify_reduce')?this.reduceEditor:this.mapEditor;
- var beautifiedCode = beautify(targetEditor.getValue());
- targetEditor.setValue(beautifiedCode);
- },
- cleanup: function () {
- this.mapEditor && this.mapEditor.remove();
- this.reduceEditor && this.reduceEditor.remove();
- }
- });
Views.JumpToDoc = FauxtonAPI.View.extend({
template: "addons/documents/templates/jumpdoc",
@@ -1782,56 +577,7 @@ function(app, FauxtonAPI, Components, Documents, Views, Databases, pouchdb,
});
-
- Views.Changes = FauxtonAPI.View.extend({
- template: "addons/documents/templates/changes",
-
- initialize: function () {
- this.listenTo( this.model.changes, 'sync', this.render);
- this.listenTo( this.model.changes, 'cachesync', this.render);
- },
-
- events: {
- "click button.js-toggle-json": "toggleJson"
- },
-
- toggleJson: function(event) {
- event.preventDefault();
-
- var $button = this.$(event.target),
- $container = $button.closest('.change-box').find(".js-json-container");
-
- if (!$container.is(":visible")) {
- $button
- .text("Close JSON")
- .addClass("btn-secondary")
- .removeClass("btn-primary");
- } else {
- $button.text("View JSON")
- .addClass("btn-primary")
- .removeClass("btn-secondary");
- }
-
- $container.slideToggle();
- },
-
- establish: function() {
- return [ this.model.changes.fetchOnce({prefill: true})];
- },
- serialize: function () {
- return {
- changes: this.model.changes.toJSON(),
- database: this.model
- };
- },
-
- afterRender: function(){
- prettyPrint();
- ZeroClipboard.config({ moviePath: "/assets/js/plugins/zeroclipboard/ZeroClipboard.swf" });
- var client = new ZeroClipboard(this.$(".js-copy"));
- }
- });
Views.DdocInfo = FauxtonAPI.View.extend({
template: "addons/documents/templates/ddoc_info",