{"action":"created","comment":{"url":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/comments/38334319","id":38334319,"diff_hunk":"@@ -0,0 +1,540 @@\n+define([\n+  'api',\n+  'app',\n+  'react',\n+  'addons/documents/doc-editor/actions',\n+  'addons/documents/doc-editor/stores',\n+  'addons/fauxton/components.react',\n+  'addons/components/react-components.react',\n+  'helpers'\n+], function (FauxtonAPI, app, React, Actions, Stores, FauxtonComponents, GeneralComponents, Helpers) {\n+\n+  var store = Stores.docEditorStore;\n+\n+\n+  var DocEditorController = React.createClass({\n+\n+    getInitialState: function () {\n+      return this.getStoreState();\n+    },\n+\n+    getStoreState: function () {\n+      return {\n+        isLoading: store.isLoading(),\n+        doc: store.getDoc(),\n+        cloneDocModalVisible: store.isCloneDocModalVisible(),\n+        uploadModalVisible: store.isUploadModalVisible(),\n+        deleteDocModalVisible: store.isDeleteDocModalVisible()\n+      };\n+    },\n+\n+    getDefaultProps: function () {\n+      return {\n+        database: {},\n+        previousPage: '',\n+        isNewDoc: false\n+      };\n+    },\n+\n+    getCodeEditor: function () {\n+      if (this.state.isLoading) {\n+        return (<GeneralComponents.LoadLines />);\n+      }\n+\n+      var code = JSON.stringify(this.state.doc.attributes, null, '  ');\n+      var editorCommands = [{\n+        name: 'save',\n+        bindKey: { win: 'Ctrl-S', mac: 'Ctrl-S' },\n+        exec: this.saveDoc\n+      }];\n+\n+      return (\n+        <GeneralComponents.CodeEditor\n+          id=\"docEditor\"\n+          ref=\"docEditor\"\n+          defaultCode={code}\n+          mode=\"json\"\n+          autoFocus={true}\n+          editorCommands={editorCommands}\n+          notifyUnsavedChanges={true}\n+          stringEditModalEnabled={true}\n+          change={this.onChangeDoc} />\n+      );\n+    },\n+\n+    onChangeDoc: function (doc) {\n+      // super ugly, but necessary. This tells the user they can't delete the _id or _rev fields as they type and actually\n+      // prevents it from being removed in the editable doc\n+      var json;\n+      try {\n+        json = JSON.parse(doc);\n+      } catch (exception) {\n+        return;\n+      }\n+\n+      var keyChecked = ['_id'];\n+      if (this.state.doc.get('_rev')) {\n+        keyChecked.push('_rev');\n+      }\n+      if (_.isEmpty(_.difference(keyChecked, _.keys(json)))) {\n+        return;\n+      }\n+\n+      this.getEditor().setReadOnly(true);\n+      setTimeout(function () { this.getEditor().setReadOnly(false); }.bind(this), 400);\n+\n+      // extend ensures _id stays at the top of the editor doc\n+      json = _.extend({ _id: this.state.doc.id, _rev: this.state.doc.get('_rev') }, json);\n+      this.getEditor().setValue(JSON.stringify(json, null, '  '));\n+\n+      FauxtonAPI.addNotification({\n+        type: 'error',\n+        msg: \"Cannot remove a document's id or revision.\",\n+        clear: true\n+      });\n+    },\n+\n+    componentDidMount: function () {\n+      store.on('change', this.onChange, this);\n+    },\n+\n+    componentWillUnmount: function () {\n+      store.off('change', this.onChange);\n+    },\n+\n+    onChange: function () {\n+      if (this.isMounted()) {\n+        this.setState(this.getStoreState());\n+      }\n+    },\n+\n+    saveDoc: function () {\n+      Actions.saveDoc(this.state.doc, this.checkDocIsValid(), this.onSaveComplete);\n+    },\n+\n+    onSaveComplete: function (json) {\n+      this.getEditor().setValue(json);\n+      this.getEditor().clearChanges();\n+\n+      // the save action updates the doc. This ensures the button row then shows the appropriate buttons\n+      this.forceUpdate();\n+    },\n+\n+    hideDeleteDocModal: function () {\n+      Actions.hideDeleteDocModal();\n+    },\n+\n+    deleteDoc: function () {\n+      Actions.hideDeleteDocModal();\n+      Actions.deleteDoc(this.state.doc);\n+    },\n+\n+    getEditor: function () {\n+      return (this.refs.docEditor) ? this.refs.docEditor.getEditor() : null;\n+    },\n+\n+    checkDocIsValid: function () {\n+      if (this.getEditor().hasErrors()) {\n+        return false;\n+      }\n+      var json = JSON.parse(this.getEditor().getValue());\n+      this.state.doc.clear().set(json, { validate: true });\n+\n+      return !this.state.doc.validationError;\n+    },\n+\n+    clearChanges: function () {\n+      this.refs.docEditor.clearChanges();\n+    },\n+\n+    getButtonRow: function () {\n+      if (this.props.isNewDoc) {\n+        return false;\n+      }\n+      return (\n+        <div>\n+          <AttachmentsPanelButton doc={this.state.doc} isLoading={this.state.isLoading} />\n+          <div className=\"doc-editor-extension-icons\"></div>\n+          <PanelButton title=\"Upload Attachment\" iconClass=\"icon-circle-arrow-up\" onClick={Actions.showUploadModal} />\n+          <PanelButton title=\"Clone Document\" iconClass=\"icon-repeat\" onClick={Actions.showCloneDocModal} />\n+          <PanelButton title=\"Delete\" iconClass=\"icon-trash\" onClick={Actions.showDeleteDocModal} />\n+        </div>\n+      );\n+    },\n+\n+    uploadComplete: function () {\n+      Actions.initDocEditor({\n+        doc: this.state.doc,\n+        onLoaded: function () {\n+          this.getEditor().setValue(JSON.stringify(this.state.doc.attributes, null, '  '));\n+          this.refs.uploadModal.reset();","path":"app/addons/documents/doc-editor/components.react.jsx","position":170,"original_position":170,"commit_id":"0717861b03a640ea400582960a8144c84b5832d7","original_commit_id":"0717861b03a640ea400582960a8144c84b5832d7","user":{"login":"benkeen","id":512116,"avatar_url":"https://avatars.githubusercontent.com/u/512116?v=3","gravatar_id":"","url":"https://api.github.com/users/benkeen","html_url":"https://github.com/benkeen","followers_url":"https://api.github.com/users/benkeen/followers","following_url":"https://api.github.com/users/benkeen/following{/other_user}","gists_url":"https://api.github.com/users/benkeen/gists{/gist_id}","starred_url":"https://api.github.com/users/benkeen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benkeen/subscriptions","organizations_url":"https://api.github.com/users/benkeen/orgs","repos_url":"https://api.github.com/users/benkeen/repos","events_url":"https://api.github.com/users/benkeen/events{/privacy}","received_events_url":"https://api.github.com/users/benkeen/received_events","type":"User","site_admin":false},"body":"Very good, I didn't think of that. I think the parent component (which passes the onSuccess method to the upload component) could listen to a published \"done\" msg and do whatever it needs there. Much cleaner.","created_at":"2015-08-31T17:14:35Z","updated_at":"2015-08-31T17:14:35Z","html_url":"https://github.com/apache/couchdb-fauxton/pull/475#discussion_r38334319","pull_request_url":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/475","_links":{"self":{"href":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/comments/38334319"},"html":{"href":"https://github.com/apache/couchdb-fauxton/pull/475#discussion_r38334319"},"pull_request":{"href":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/475"}}},"pull_request":{"url":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/475","id":38589851,"html_url":"https://github.com/apache/couchdb-fauxton/pull/475","diff_url":"https://github.com/apache/couchdb-fauxton/pull/475.diff","patch_url":"https://github.com/apache/couchdb-fauxton/pull/475.patch","issue_url":"https://api.github.com/repos/apache/couchdb-fauxton/issues/475","number":475,"state":"open","locked":false,"title":"Reactifying full page doc editor","user":{"login":"benkeen","id":512116,"avatar_url":"https://avatars.githubusercontent.com/u/512116?v=3","gravatar_id":"","url":"https://api.github.com/users/benkeen","html_url":"https://github.com/benkeen","followers_url":"https://api.github.com/users/benkeen/followers","following_url":"https://api.github.com/users/benkeen/following{/other_user}","gists_url":"https://api.github.com/users/benkeen/gists{/gist_id}","starred_url":"https://api.github.com/users/benkeen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benkeen/subscriptions","organizations_url":"https://api.github.com/users/benkeen/orgs","repos_url":"https://api.github.com/users/benkeen/repos","events_url":"https://api.github.com/users/benkeen/events{/privacy}","received_events_url":"https://api.github.com/users/benkeen/received_events","type":"User","site_admin":false},"body":"This PR converts the full page document editor to React. Most of it\r\nis just for the React conversion + dropping the Backbone code (includes\r\nremoving the old Backbone code editor), but there are also a few \r\nimprovements:\r\n\r\n- File uploads now work locally\r\n- New docs hide the button row altogether (attachments, delete, clone) until saved.\r\n- the string edit modal has been moved to be an optional subelements of `<CodeEditor />` enabled with a boolean (omited by default)\r\n- various fixes to the string edit modal","created_at":"2015-06-25T20:31:04Z","updated_at":"2015-08-31T17:14:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"cb439522add3f7b6a52d95a72ecb76b00da71aea","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/475/commits","review_comments_url":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/475/comments","review_comment_url":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/comments{/number}","comments_url":"https://api.github.com/repos/apache/couchdb-fauxton/issues/475/comments","statuses_url":"https://api.github.com/repos/apache/couchdb-fauxton/statuses/0717861b03a640ea400582960a8144c84b5832d7","head":{"label":"benkeen:fpde-react","ref":"fpde-react","sha":"0717861b03a640ea400582960a8144c84b5832d7","user":{"login":"benkeen","id":512116,"avatar_url":"https://avatars.githubusercontent.com/u/512116?v=3","gravatar_id":"","url":"https://api.github.com/users/benkeen","html_url":"https://github.com/benkeen","followers_url":"https://api.github.com/users/benkeen/followers","following_url":"https://api.github.com/users/benkeen/following{/other_user}","gists_url":"https://api.github.com/users/benkeen/gists{/gist_id}","starred_url":"https://api.github.com/users/benkeen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benkeen/subscriptions","organizations_url":"https://api.github.com/users/benkeen/orgs","repos_url":"https://api.github.com/users/benkeen/repos","events_url":"https://api.github.com/users/benkeen/events{/privacy}","received_events_url":"https://api.github.com/users/benkeen/received_events","type":"User","site_admin":false},"repo":{"id":24124373,"name":"couchdb-fauxton","full_name":"benkeen/couchdb-fauxton","owner":{"login":"benkeen","id":512116,"avatar_url":"https://avatars.githubusercontent.com/u/512116?v=3","gravatar_id":"","url":"https://api.github.com/users/benkeen","html_url":"https://github.com/benkeen","followers_url":"https://api.github.com/users/benkeen/followers","following_url":"https://api.github.com/users/benkeen/following{/other_user}","gists_url":"https://api.github.com/users/benkeen/gists{/gist_id}","starred_url":"https://api.github.com/users/benkeen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/benkeen/subscriptions","organizations_url":"https://api.github.com/users/benkeen/orgs","repos_url":"https://api.github.com/users/benkeen/repos","events_url":"https://api.github.com/users/benkeen/events{/privacy}","received_events_url":"https://api.github.com/users/benkeen/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/benkeen/couchdb-fauxton","description":"Mirror of Apache CouchDB","fork":true,"url":"https://api.github.com/repos/benkeen/couchdb-fauxton","forks_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/forks","keys_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/keys{/key_id}","collaborators_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/teams","hooks_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/hooks","issue_events_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/issues/events{/number}","events_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/events","assignees_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/assignees{/user}","branches_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/branches{/branch}","tags_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/tags","blobs_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/git/refs{/sha}","trees_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/git/trees{/sha}","statuses_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/statuses/{sha}","languages_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/languages","stargazers_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/stargazers","contributors_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/contributors","subscribers_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/subscribers","subscription_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/subscription","commits_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/commits{/sha}","git_commits_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/git/commits{/sha}","comments_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/comments{/number}","issue_comment_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/issues/comments{/number}","contents_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/contents/{+path}","compare_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/compare/{base}...{head}","merges_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/merges","archive_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/downloads","issues_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/issues{/number}","pulls_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/pulls{/number}","milestones_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/milestones{/number}","notifications_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/labels{/name}","releases_url":"https://api.github.com/repos/benkeen/couchdb-fauxton/releases{/id}","created_at":"2014-09-17T01:15:34Z","updated_at":"2015-05-07T21:40:31Z","pushed_at":"2015-08-28T22:36:49Z","git_url":"git://github.com/benkeen/couchdb-fauxton.git","ssh_url":"git@github.com:benkeen/couchdb-fauxton.git","clone_url":"https://github.com/benkeen/couchdb-fauxton.git","svn_url":"https://github.com/benkeen/couchdb-fauxton","homepage":null,"size":9143,"stargazers_count":1,"watchers_count":1,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"apache:master","ref":"master","sha":"825bbfc420e2a21151d7725a642614e0bf17d1d6","user":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"repo":{"id":15993612,"name":"couchdb-fauxton","full_name":"apache/couchdb-fauxton","owner":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/apache/couchdb-fauxton","description":"Mirror of Apache CouchDB","fork":false,"url":"https://api.github.com/repos/apache/couchdb-fauxton","forks_url":"https://api.github.com/repos/apache/couchdb-fauxton/forks","keys_url":"https://api.github.com/repos/apache/couchdb-fauxton/keys{/key_id}","collaborators_url":"https://api.github.com/repos/apache/couchdb-fauxton/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/apache/couchdb-fauxton/teams","hooks_url":"https://api.github.com/repos/apache/couchdb-fauxton/hooks","issue_events_url":"https://api.github.com/repos/apache/couchdb-fauxton/issues/events{/number}","events_url":"https://api.github.com/repos/apache/couchdb-fauxton/events","assignees_url":"https://api.github.com/repos/apache/couchdb-fauxton/assignees{/user}","branches_url":"https://api.github.com/repos/apache/couchdb-fauxton/branches{/branch}","tags_url":"https://api.github.com/repos/apache/couchdb-fauxton/tags","blobs_url":"https://api.github.com/repos/apache/couchdb-fauxton/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/apache/couchdb-fauxton/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/apache/couchdb-fauxton/git/refs{/sha}","trees_url":"https://api.github.com/repos/apache/couchdb-fauxton/git/trees{/sha}","statuses_url":"https://api.github.com/repos/apache/couchdb-fauxton/statuses/{sha}","languages_url":"https://api.github.com/repos/apache/couchdb-fauxton/languages","stargazers_url":"https://api.github.com/repos/apache/couchdb-fauxton/stargazers","contributors_url":"https://api.github.com/repos/apache/couchdb-fauxton/contributors","subscribers_url":"https://api.github.com/repos/apache/couchdb-fauxton/subscribers","subscription_url":"https://api.github.com/repos/apache/couchdb-fauxton/subscription","commits_url":"https://api.github.com/repos/apache/couchdb-fauxton/commits{/sha}","git_commits_url":"https://api.github.com/repos/apache/couchdb-fauxton/git/commits{/sha}","comments_url":"https://api.github.com/repos/apache/couchdb-fauxton/comments{/number}","issue_comment_url":"https://api.github.com/repos/apache/couchdb-fauxton/issues/comments{/number}","contents_url":"https://api.github.com/repos/apache/couchdb-fauxton/contents/{+path}","compare_url":"https://api.github.com/repos/apache/couchdb-fauxton/compare/{base}...{head}","merges_url":"https://api.github.com/repos/apache/couchdb-fauxton/merges","archive_url":"https://api.github.com/repos/apache/couchdb-fauxton/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/apache/couchdb-fauxton/downloads","issues_url":"https://api.github.com/repos/apache/couchdb-fauxton/issues{/number}","pulls_url":"https://api.github.com/repos/apache/couchdb-fauxton/pulls{/number}","milestones_url":"https://api.github.com/repos/apache/couchdb-fauxton/milestones{/number}","notifications_url":"https://api.github.com/repos/apache/couchdb-fauxton/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/apache/couchdb-fauxton/labels{/name}","releases_url":"https://api.github.com/repos/apache/couchdb-fauxton/releases{/id}","created_at":"2014-01-17T08:00:08Z","updated_at":"2015-08-24T22:20:13Z","pushed_at":"2015-08-31T16:16:22Z","git_url":"git://github.com/apache/couchdb-fauxton.git","ssh_url":"git@github.com:apache/couchdb-fauxton.git","clone_url":"https://github.com/apache/couchdb-fauxton.git","svn_url":"https://github.com/apache/couchdb-fauxton","homepage":null,"size":11733,"stargazers_count":56,"watchers_count":56,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":56,"mirror_url":"git://git.apache.org/couchdb-fauxton.git","open_issues_count":9,"forks":56,"open_issues":9,"watchers":56,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/475"},"html":{"href":"https://github.com/apache/couchdb-fauxton/pull/475"},"issue":{"href":"https://api.github.com/repos/apache/couchdb-fauxton/issues/475"},"comments":{"href":"https://api.github.com/repos/apache/couchdb-fauxton/issues/475/comments"},"review_comments":{"href":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/475/comments"},"review_comment":{"href":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/apache/couchdb-fauxton/pulls/475/commits"},"statuses":{"href":"https://api.github.com/repos/apache/couchdb-fauxton/statuses/0717861b03a640ea400582960a8144c84b5832d7"}}}}