EditorManager.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. max-len,
  5. no-return-assign,
  6. no-undef,
  7. */
  8. // TODO: This file was created by bulk-decaffeinate.
  9. // Fix any style issues and re-enable lint.
  10. /*
  11. * decaffeinate suggestions:
  12. * DS102: Remove unnecessary code created because of implicit returns
  13. * DS206: Consider reworking classes to avoid initClass
  14. * DS207: Consider shorter variations of null checks
  15. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  16. */
  17. define([
  18. 'ide/editor/Document',
  19. 'ide/editor/components/spellMenu',
  20. 'ide/editor/directives/aceEditor',
  21. 'ide/editor/directives/toggleSwitch',
  22. 'ide/editor/controllers/SavingNotificationController'
  23. ], function(Document) {
  24. let EditorManager
  25. return (EditorManager = (function() {
  26. EditorManager = class EditorManager {
  27. static initClass() {
  28. this.prototype._syncTimeout = null
  29. }
  30. constructor(ide, $scope, localStorage) {
  31. this.ide = ide
  32. this.$scope = $scope
  33. this.localStorage = localStorage
  34. this.$scope.editor = {
  35. sharejs_doc: null,
  36. open_doc_id: null,
  37. open_doc_name: null,
  38. opening: true,
  39. trackChanges: false,
  40. wantTrackChanges: false,
  41. showRichText: this.showRichText()
  42. }
  43. this.$scope.$on('entity:selected', (event, entity) => {
  44. if (this.$scope.ui.view !== 'history' && entity.type === 'doc') {
  45. return this.openDoc(entity)
  46. }
  47. })
  48. this.$scope.$on('entity:deleted', (event, entity) => {
  49. if (this.$scope.editor.open_doc_id === entity.id) {
  50. if (!this.$scope.project.rootDoc_id) {
  51. return
  52. }
  53. const doc = this.ide.fileTreeManager.findEntityById(
  54. this.$scope.project.rootDoc_id
  55. )
  56. if (doc == null) {
  57. return
  58. }
  59. return this.openDoc(doc)
  60. }
  61. })
  62. let initialized = false
  63. this.$scope.$on('file-tree:initialized', () => {
  64. if (!initialized) {
  65. initialized = true
  66. return this.autoOpenDoc()
  67. }
  68. })
  69. this.$scope.$on('flush-changes', () => {
  70. return Document.flushAll()
  71. })
  72. this.$scope.$watch('editor.wantTrackChanges', value => {
  73. if (value == null) {
  74. return
  75. }
  76. return this._syncTrackChangesState(this.$scope.editor.sharejs_doc)
  77. })
  78. }
  79. showRichText() {
  80. return (
  81. this.localStorage(`editor.mode.${this.$scope.project_id}`) ===
  82. 'rich-text'
  83. )
  84. }
  85. autoOpenDoc() {
  86. const open_doc_id =
  87. this.ide.localStorage(`doc.open_id.${this.$scope.project_id}`) ||
  88. this.$scope.project.rootDoc_id
  89. if (open_doc_id == null) {
  90. return
  91. }
  92. const doc = this.ide.fileTreeManager.findEntityById(open_doc_id)
  93. if (doc == null) {
  94. return
  95. }
  96. return this.openDoc(doc)
  97. }
  98. openDocId(doc_id, options) {
  99. if (options == null) {
  100. options = {}
  101. }
  102. const doc = this.ide.fileTreeManager.findEntityById(doc_id)
  103. if (doc == null) {
  104. return
  105. }
  106. return this.openDoc(doc, options)
  107. }
  108. openDoc(doc, options) {
  109. if (options == null) {
  110. options = {}
  111. }
  112. sl_console.log(`[openDoc] Opening ${doc.id}`)
  113. this.$scope.ui.view = 'editor'
  114. const done = () => {
  115. if (options.gotoLine != null) {
  116. // allow Ace to display document before moving, delay until next tick
  117. // added delay to make this happen later that gotoStoredPosition in
  118. // CursorPositionManager
  119. return setTimeout(() => {
  120. return this.$scope.$broadcast(
  121. 'editor:gotoLine',
  122. options.gotoLine,
  123. options.gotoColumn
  124. )
  125. }, 0)
  126. } else if (options.gotoOffset != null) {
  127. return setTimeout(() => {
  128. return this.$scope.$broadcast(
  129. 'editor:gotoOffset',
  130. options.gotoOffset
  131. )
  132. }, 0)
  133. }
  134. }
  135. if (doc.id === this.$scope.editor.open_doc_id && !options.forceReopen) {
  136. this.$scope.$apply(() => {
  137. return done()
  138. })
  139. return
  140. }
  141. this.$scope.editor.open_doc_id = doc.id
  142. this.$scope.editor.open_doc_name = doc.name
  143. this.ide.localStorage(`doc.open_id.${this.$scope.project_id}`, doc.id)
  144. this.ide.fileTreeManager.selectEntity(doc)
  145. this.$scope.editor.opening = true
  146. return this._openNewDocument(doc, (error, sharejs_doc) => {
  147. if (error != null) {
  148. this.ide.showGenericMessageModal(
  149. 'Error opening document',
  150. 'Sorry, something went wrong opening this document. Please try again.'
  151. )
  152. return
  153. }
  154. this._syncTrackChangesState(sharejs_doc)
  155. this.$scope.$broadcast('doc:opened')
  156. return this.$scope.$apply(() => {
  157. this.$scope.editor.opening = false
  158. this.$scope.editor.sharejs_doc = sharejs_doc
  159. return done()
  160. })
  161. })
  162. }
  163. _openNewDocument(doc, callback) {
  164. if (callback == null) {
  165. callback = function(error, sharejs_doc) {}
  166. }
  167. sl_console.log('[_openNewDocument] Opening...')
  168. const current_sharejs_doc = this.$scope.editor.sharejs_doc
  169. if (current_sharejs_doc != null) {
  170. sl_console.log('[_openNewDocument] Leaving existing open doc...')
  171. current_sharejs_doc.leaveAndCleanUp()
  172. this._unbindFromDocumentEvents(current_sharejs_doc)
  173. }
  174. const new_sharejs_doc = Document.getDocument(this.ide, doc.id)
  175. return new_sharejs_doc.join(error => {
  176. if (error != null) {
  177. return callback(error)
  178. }
  179. this._bindToDocumentEvents(doc, new_sharejs_doc)
  180. return callback(null, new_sharejs_doc)
  181. })
  182. }
  183. _bindToDocumentEvents(doc, sharejs_doc) {
  184. sharejs_doc.on('error', (error, meta) => {
  185. let message
  186. if ((error != null ? error.message : undefined) != null) {
  187. ;({ message } = error)
  188. } else if (typeof error === 'string') {
  189. message = error
  190. } else {
  191. message = ''
  192. }
  193. if (/maxDocLength/.test(message)) {
  194. this.ide.showGenericMessageModal(
  195. 'Document Too Long',
  196. 'Sorry, this file is too long to be edited manually. Please upload it directly.'
  197. )
  198. } else if (/too many comments or tracked changes/.test(message)) {
  199. this.ide.showGenericMessageModal(
  200. 'Too many comments or tracked changes',
  201. 'Sorry, this file has too many comments or tracked changes. Please try accepting or rejecting some existing changes, or resolving and deleting some comments.'
  202. )
  203. } else {
  204. this.ide.socket.disconnect()
  205. this.ide.reportError(error, meta)
  206. this.ide.showGenericMessageModal(
  207. 'Out of sync',
  208. "Sorry, this file has gone out of sync and we need to do a full refresh. <br> <a href='/learn/Kb/Editor_out_of_sync_problems'>Please see this help guide for more information</a>"
  209. )
  210. }
  211. return this.openDoc(doc, { forceReopen: true })
  212. })
  213. return sharejs_doc.on('externalUpdate', update => {
  214. if (this._ignoreExternalUpdates) {
  215. return
  216. }
  217. return this.ide.showGenericMessageModal(
  218. 'Document Updated Externally',
  219. 'This document was just updated externally. Any recent changes you have made may have been overwritten. To see previous versions please look in the history.'
  220. )
  221. })
  222. }
  223. _unbindFromDocumentEvents(document) {
  224. return document.off()
  225. }
  226. getCurrentDocValue() {
  227. return this.$scope.editor.sharejs_doc != null
  228. ? this.$scope.editor.sharejs_doc.getSnapshot()
  229. : undefined
  230. }
  231. getCurrentDocId() {
  232. return this.$scope.editor.open_doc_id
  233. }
  234. startIgnoringExternalUpdates() {
  235. return (this._ignoreExternalUpdates = true)
  236. }
  237. stopIgnoringExternalUpdates() {
  238. return (this._ignoreExternalUpdates = false)
  239. }
  240. _syncTrackChangesState(doc) {
  241. let tryToggle
  242. if (doc == null) {
  243. return
  244. }
  245. if (this._syncTimeout != null) {
  246. clearTimeout(this._syncTimeout)
  247. this._syncTimeout = null
  248. }
  249. const want = this.$scope.editor.wantTrackChanges
  250. const have = doc.getTrackingChanges()
  251. if (want === have) {
  252. this.$scope.editor.trackChanges = want
  253. return
  254. }
  255. return (tryToggle = () => {
  256. const saved =
  257. doc.getInflightOp() == null && doc.getPendingOp() == null
  258. if (saved) {
  259. doc.setTrackingChanges(want)
  260. return this.$scope.$apply(() => {
  261. return (this.$scope.editor.trackChanges = want)
  262. })
  263. } else {
  264. return (this._syncTimeout = setTimeout(tryToggle, 100))
  265. }
  266. })()
  267. }
  268. }
  269. EditorManager.initClass()
  270. return EditorManager
  271. })())
  272. })