FileTreeManager.coffee 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. define [
  2. "models/Doc"
  3. "models/File"
  4. "models/Folder"
  5. "file-tree/FileTreeView"
  6. "file-tree/FolderView"
  7. "file-tree/DeletedDocsFolderView"
  8. "utils/Effects"
  9. "utils/Modal"
  10. "libs/backbone"
  11. "libs/jquery.storage"
  12. ], (Doc, File, Folder, FileTreeView, FolderView, DeletedDocsFolderView, Effects, Modal) ->
  13. class FileTreeManager
  14. constructor: (@ide) ->
  15. _.extend(@, Backbone.Events)
  16. @views = {}
  17. @multiSelectedEntities = []
  18. @ide.on "afterJoinProject", (@project) =>
  19. @populateFileTree()
  20. @makeReadWriteIfAllowed()
  21. @project_id = @project.id
  22. if @ide.editor?.current_doc_id?
  23. @openDoc(@ide.editor.current_doc_id)
  24. else if location.hash.length > 1
  25. fileName = location.hash.slice(1)
  26. @openDocByPath(fileName)
  27. else if openDoc_id = $.localStorage("doc.open_id.#{@project_id}") and @getEntity(openDoc_id)
  28. @openDoc(openDoc_id)
  29. else if @project.get("rootDoc_id")?
  30. @openDoc(project.get("rootDoc_id"))
  31. else
  32. $('#settings').click()
  33. @view = new FileTreeView(@)
  34. @ide.sideBarView.addLink
  35. identifier: "file-tree"
  36. element: @view.$el
  37. prepend: true
  38. @view.render()
  39. @listenForUpdates()
  40. populateFileTree: () ->
  41. @view.bindToRootFolder(@project.get("rootFolder"))
  42. if @deletedDocsView?
  43. @deletedDocsView.$el.remove()
  44. @deletedDocsView = new DeletedDocsFolderView(model: @project.get("deletedDocs"), manager: @)
  45. @deletedDocsView.render()
  46. $("#sections").append(@deletedDocsView.$el)
  47. @hideDeletedDocs()
  48. listenForUpdates: () ->
  49. @ide.socket.on 'reciveNewDoc', (folder_id, doc) =>
  50. @addEntityToFolder(
  51. new Doc(id: doc._id, name: doc.name)
  52. folder_id
  53. )
  54. @ide.socket.on 'reciveNewFolder', (folder_id, folder) =>
  55. @addEntityToFolder(
  56. new Folder(id: folder._id, name: folder.name)
  57. folder_id
  58. )
  59. @ide.socket.on 'reciveNewFile', (folder_id, file) =>
  60. @addEntityToFolder(
  61. new File(id: file._id, name: file.name)
  62. folder_id
  63. )
  64. @ide.socket.on 'removeEntity', (entity_id) =>
  65. @onDeleteEntity(entity_id)
  66. @ide.socket.on 'reciveEntityRename', (entity_id, newName) =>
  67. @onRenameEntity(entity_id, newName)
  68. @ide.socket.on 'reciveEntityMove', (entity_id, folder_id) =>
  69. @onMoveEntity(entity_id, folder_id)
  70. registerView: (entity_id, view) ->
  71. @views[entity_id] = view
  72. addEntityToFolder: (entity, folder_id) ->
  73. folder = @views[folder_id].model
  74. children = folder.get("children")
  75. children.add(entity)
  76. openDoc: (doc, line) ->
  77. return if !doc?
  78. doc_id = doc.id or doc
  79. @trigger "open:doc", doc_id, line: line
  80. @selectEntity(doc_id)
  81. $.localStorage "doc.open_id.#{@project_id}", doc_id
  82. openDocByPath: (path, line) ->
  83. doc_id = @getDocIdOfPath(path)
  84. return null if !doc_id?
  85. @openDoc(doc_id, line)
  86. openFile: (file) ->
  87. @trigger "open:file", file
  88. @selectEntity(file.id)
  89. openFolder: (folder) ->
  90. @selectEntity(folder.id)
  91. selectEntity: (entity_id) ->
  92. if @views[@selected_entity_id]?
  93. @views[@selected_entity_id].deselect()
  94. @selected_entity_id = entity_id
  95. @ide.sideBarView.deselectAll()
  96. @views[entity_id]?.select()
  97. getEntity: (entity_id, options = {include_deleted: false}) ->
  98. model = @views[entity_id]?.model
  99. if !model? or (model.get("deleted") and !options.include_deleted)
  100. return
  101. else
  102. return model
  103. getSelectedEntity: () -> @getEntity(@selected_entity_id)
  104. getSelectedEntityId: () -> @getSelectedEntity()?.id
  105. getCurrentFolder: () ->
  106. selected_entity = @getSelectedEntity()
  107. if !selected_entity?
  108. return @project.get("rootFolder")
  109. else if selected_entity instanceof Folder
  110. return selected_entity
  111. else
  112. return selected_entity.collection.parentFolder
  113. getDocIdOfPath: (path) ->
  114. parts = path.split("/")
  115. folder = @project.get("rootFolder")
  116. lastPart = parts.pop()
  117. getChildWithName = (folder, name) ->
  118. return folder if name == "."
  119. foundChild = null
  120. for child in folder.get("children").models
  121. if child.get("name") == name
  122. foundChild = child
  123. return foundChild
  124. for part in parts
  125. folder = getChildWithName(folder, part)
  126. return null if !folder or !(folder instanceof Folder)
  127. doc = getChildWithName(folder, lastPart)
  128. return null if !doc or !(doc instanceof Doc)
  129. return doc.id
  130. getPathOfEntityId: (entity_id) ->
  131. entity = @getEntity(entity_id)
  132. return if !entity?
  133. path = entity.get("name")
  134. while (entity = entity.collection?.parentFolder)
  135. if entity.collection?
  136. # it's not the root folder so keep going
  137. path = entity.get("name") + "/" + path
  138. return path
  139. getRootFolderPath: () ->
  140. rootFilePath = @getPathOfEntityId(@project.get("rootDoc_id"))
  141. return rootFilePath.split("/").slice(0, -1).join("/")
  142. getNameOfEntityId: (entity_id) ->
  143. entity = @getEntity(entity_id)
  144. return if !entity?
  145. return entity.get("name")
  146. # RENAMING
  147. renameSelected: () ->
  148. entity_id = @getSelectedEntityId()
  149. return if !entity_id?
  150. @views[entity_id]?.startRename()
  151. ga('send', 'event', 'editor-interaction', 'renameEntity', "topMenu")
  152. renameEntity: (entity, name) ->
  153. name = name?.trim()
  154. @ide.socket.emit 'renameEntity', entity.id, entity.get("type"), name
  155. entity.set("name", name)
  156. onRenameEntity: (entity_id, name) ->
  157. @getEntity(entity_id)?.set("name", name)
  158. # MOVING
  159. onMoveEntity: (entity_id, folder_id) ->
  160. entity = @getEntity(entity_id)
  161. destFolder = @getEntity(folder_id)
  162. return if !entity? or !destFolder?
  163. if entity.collection == destFolder.get("children")
  164. # Already in parent folder
  165. return
  166. return if @_isParent(entity_id, folder_id)
  167. entity.collection.remove(entity)
  168. destFolder.get("children").add(entity)
  169. _isParent: (parent_folder_id, child_folder_id) ->
  170. childFolder = @getEntity(child_folder_id)
  171. return false unless childFolder? and childFolder instanceof Folder
  172. parentIds = childFolder.getParentFolderIds()
  173. if parentIds.indexOf(parent_folder_id) > -1
  174. return true
  175. else
  176. return false
  177. moveEntity: (entity_id, folder_id, type) ->
  178. return if @_isParent(entity_id, folder_id)
  179. @ide.socket.emit 'moveEntity', entity_id, folder_id, type
  180. @onMoveEntity(entity_id, folder_id)
  181. # CREATING
  182. showNewEntityModal: (type, defaultName, callback = (name) ->) ->
  183. el = $($("#newEntityModalTemplate").html())
  184. input = el.find("input")
  185. create = _.once () =>
  186. name = input.val()?.trim()
  187. if name != ""
  188. callback(name)
  189. modal = new Modal
  190. title: "New #{type}"
  191. el: el
  192. buttons: [{
  193. text: "Cancel"
  194. }, {
  195. text: "Create"
  196. callback: create
  197. class: "btn-primary"
  198. }]
  199. input.on "keydown", (e) ->
  200. if e.keyCode == 13 # Enter
  201. create()
  202. modal.remove()
  203. input.val(defaultName.replace("|", ""))
  204. if input[0].setSelectionRange?
  205. # value is "name.tex"
  206. input[0].setSelectionRange(0, defaultName.indexOf("|"))
  207. showNewDocModal: (parentFolder = @getCurrentFolder()) ->
  208. return if !parentFolder?
  209. @showNewEntityModal "Document", "name|.tex", (name) =>
  210. @addDocToFolder parentFolder, name
  211. showNewFolderModal: (parentFolder = @getCurrentFolder()) ->
  212. return if !parentFolder?
  213. @showNewEntityModal "Folder", "name|", (name) =>
  214. @addFolderToFolder parentFolder, name
  215. showUploadFileModal: (parentFolder = @getCurrentFolder()) ->
  216. return if !parentFolder?
  217. @ide.fileUploadManager.showUploadDialog parentFolder.id
  218. addDoc: (folder_id, name) ->
  219. @ide.socket.emit 'addDoc', folder_id, name
  220. addDocToFolder: (parentFolder, name) ->
  221. @addDoc parentFolder.id, name
  222. addFolder: (parent_folder_id, name) ->
  223. @ide.socket.emit 'addFolder', parent_folder_id, name
  224. addFolderToFolder: (parentFolder, name) ->
  225. return if !parentFolder?
  226. @addFolder parentFolder.id, name
  227. # DELETING
  228. confirmDelete: (entity) ->
  229. ga('send', 'event', 'editor-interaction', 'deleteEntity', "topMenu")
  230. Modal.createModal
  231. title: "Confirm Deletion"
  232. message: "Are you sure you want to delete <strong>#{entity.get("name")}</strong>?"
  233. buttons: [{
  234. text: "Cancel"
  235. class: "btn"
  236. },{
  237. text: "Delete"
  238. class: "btn btn-danger"
  239. callback: () => @_doDelete(entity)
  240. }]
  241. confirmDeleteOfSelectedEntity: () ->
  242. entity = @getSelectedEntity()
  243. return if !entity?
  244. @confirmDelete(entity)
  245. _doDelete: (entity) ->
  246. @ide.socket.emit 'deleteEntity', entity.id, entity.get("type")
  247. @onDeleteEntity entity.id
  248. onDeleteEntity: (entity_id) ->
  249. entity = @getEntity(entity_id)
  250. return if !entity?
  251. entity.set("deleted", true)
  252. entity.collection?.remove(entity)
  253. delete @views[entity_id]
  254. # Do this after the remove so that it's never in two places at once
  255. # and so that it doesn't get reset by deleting from @views
  256. if entity.get("type") == "doc"
  257. @project.get("deletedDocs").get("children").add entity
  258. setLabels: (labels) ->
  259. @view.setLabels(labels)
  260. @deletedDocsView.setLabels(labels)
  261. showDeletedDocs: () ->
  262. if @project.get("deletedDocs").get("children").length > 0
  263. @deletedDocsView.$el.show()
  264. hideDeletedDocs: () ->
  265. @deletedDocsView.$el.hide()
  266. makeReadOnly: () ->
  267. for id, view of @views or []
  268. view.makeReadOnly?()
  269. makeReadWrite: () ->
  270. for id, view of @views or []
  271. view.makeReadWrite?()
  272. makeReadWriteIfAllowed: () ->
  273. if @ide.isAllowedToDoIt("readAndWrite")
  274. @makeReadWrite()
  275. else
  276. @makeReadOnly()