| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- define [
- "ide/file-tree/directives/fileEntity"
- "ide/file-tree/directives/draggable"
- "ide/file-tree/directives/droppable"
- "ide/file-tree/controllers/FileTreeController"
- "ide/file-tree/controllers/FileTreeEntityController"
- "ide/file-tree/controllers/FileTreeFolderController"
- "ide/file-tree/controllers/FileTreeRootFolderController"
- ], () ->
- class FileTreeManager
- constructor: (@ide, @$scope) ->
- @$scope.$on "project:joined", =>
- @loadRootFolder()
- @loadDeletedDocs()
- @$scope.$emit "file-tree:initialized"
- @$scope.$watch "rootFolder", (rootFolder) =>
- if rootFolder?
- @recalculateDocList()
- @_bindToSocketEvents()
- @$scope.multiSelectedCount = 0
- $(document).on "click", =>
- @clearMultiSelectedEntities()
- @$scope.$digest()
- _bindToSocketEvents: () ->
- @ide.socket.on "reciveNewDoc", (parent_folder_id, doc) =>
- parent_folder = @findEntityById(parent_folder_id) or @$scope.rootFolder
- @$scope.$apply () =>
- parent_folder.children.push {
- name: doc.name
- id: doc._id
- type: "doc"
- }
- @recalculateDocList()
- @ide.socket.on "reciveNewFile", (parent_folder_id, file, source, linkedFileData) =>
- parent_folder = @findEntityById(parent_folder_id) or @$scope.rootFolder
- @$scope.$apply () =>
- parent_folder.children.push {
- name: file.name
- id: file._id
- type: "file",
- linkedFileData: linkedFileData
- }
- @recalculateDocList()
- @ide.socket.on "reciveNewFolder", (parent_folder_id, folder) =>
- parent_folder = @findEntityById(parent_folder_id) or @$scope.rootFolder
- @$scope.$apply () =>
- parent_folder.children.push {
- name: folder.name
- id: folder._id
- type: "folder"
- children: []
- }
- @recalculateDocList()
- @ide.socket.on "reciveEntityRename", (entity_id, name) =>
- entity = @findEntityById(entity_id)
- return if !entity?
- @$scope.$apply () =>
- entity.name = name
- @recalculateDocList()
- @ide.socket.on "removeEntity", (entity_id) =>
- entity = @findEntityById(entity_id)
- return if !entity?
- @$scope.$apply () =>
- @_deleteEntityFromScope entity
- @recalculateDocList()
- @$scope.$broadcast "entity:deleted", entity
- @ide.socket.on "reciveEntityMove", (entity_id, folder_id) =>
- entity = @findEntityById(entity_id)
- folder = @findEntityById(folder_id)
- @$scope.$apply () =>
- @_moveEntityInScope(entity, folder)
- @recalculateDocList()
- selectEntity: (entity) ->
- @selected_entity_id = entity.id # For reselecting after a reconnect
- @ide.fileTreeManager.forEachEntity (entity) ->
- entity.selected = false
- entity.selected = true
- toggleMultiSelectEntity: (entity) ->
- entity.multiSelected = !entity.multiSelected
- @$scope.multiSelectedCount = @multiSelectedCount()
- multiSelectedCount: () ->
- count = 0
- @forEachEntity (entity) ->
- if entity.multiSelected
- count++
- return count
- getMultiSelectedEntities: () ->
- entities = []
- @forEachEntity (e) ->
- if e.multiSelected
- entities.push e
- return entities
- getMultiSelectedEntityChildNodes: () ->
- entities = @getMultiSelectedEntities()
- paths = {}
- for entity in entities
- paths[@getEntityPath(entity)] = entity
- prefixes = {}
- for path, entity of paths
- parts = path.split("/")
- if parts.length <= 1
- continue
- else
- # Record prefixes a/b/c.tex -> 'a' and 'a/b'
- for i in [1..(parts.length - 1)]
- prefixes[parts.slice(0,i).join("/")] = true
- child_entities = []
- for path, entity of paths
- # If the path is in the prefixes, then it's a parent folder and
- # should be ignore
- if !prefixes[path]?
- child_entities.push entity
- return child_entities
- clearMultiSelectedEntities: () ->
- return if @$scope.multiSelectedCount == 0 # Be efficient, this is called a lot on 'click'
- @forEachEntity (entity) ->
- entity.multiSelected = false
- @$scope.multiSelectedCount = 0
- multiSelectSelectedEntity: () ->
- @findSelectedEntity()?.multiSelected = true
- existsInFolder: (folder_id, name) ->
- folder = @findEntityById(folder_id)
- return false if !folder?
- entity = @_findEntityByPathInFolder(folder, name)
- return entity?
- findSelectedEntity: () ->
- selected = null
- @forEachEntity (entity) ->
- selected = entity if entity.selected
- return selected
- findEntityById: (id, options = {}) ->
- return @$scope.rootFolder if @$scope.rootFolder.id == id
- entity = @_findEntityByIdInFolder @$scope.rootFolder, id
- return entity if entity?
- if options.includeDeleted
- for entity in @$scope.deletedDocs
- return entity if entity.id == id
- return null
- _findEntityByIdInFolder: (folder, id) ->
- for entity in folder.children or []
- if entity.id == id
- return entity
- else if entity.children?
- result = @_findEntityByIdInFolder(entity, id)
- return result if result?
- return null
- findEntityByPath: (path) ->
- @_findEntityByPathInFolder @$scope.rootFolder, path
- _findEntityByPathInFolder: (folder, path) ->
- if !path? or !folder?
- return null
- if path == ""
- return folder
- parts = path.split("/")
- name = parts.shift()
- rest = parts.join("/")
- if name == "."
- return @_findEntityByPathInFolder(folder, rest)
- for entity in folder.children
- if entity.name == name
- if rest == ""
- return entity
- else if entity.type == "folder"
- return @_findEntityByPathInFolder(entity, rest)
- return null
- forEachEntity: (callback = (entity, parent_folder, path) ->) ->
- @_forEachEntityInFolder(@$scope.rootFolder, null, callback)
- for entity in @$scope.deletedDocs or []
- callback(entity)
- _forEachEntityInFolder: (folder, path, callback) ->
- for entity in folder.children or []
- if path?
- childPath = path + "/" + entity.name
- else
- childPath = entity.name
- callback(entity, folder, childPath)
- if entity.children?
- @_forEachEntityInFolder(entity, childPath, callback)
- getEntityPath: (entity) ->
- @_getEntityPathInFolder @$scope.rootFolder, entity
- _getEntityPathInFolder: (folder, entity) ->
- for child in folder.children or []
- if child == entity
- return entity.name
- else if child.type == "folder"
- path = @_getEntityPathInFolder(child, entity)
- if path?
- return child.name + "/" + path
- return null
- getRootDocDirname: () ->
- rootDoc = @findEntityById @$scope.project.rootDoc_id
- return if !rootDoc?
- return @_getEntityDirname(rootDoc)
- _getEntityDirname: (entity) ->
- path = @getEntityPath(entity)
- return if !path?
- return path.split("/").slice(0, -1).join("/")
- _findParentFolder: (entity) ->
- dirname = @_getEntityDirname(entity)
- return if !dirname?
- return @findEntityByPath(dirname)
- loadRootFolder: () ->
- @$scope.rootFolder = @_parseFolder(@$scope?.project?.rootFolder[0])
- _parseFolder: (rawFolder) ->
- folder = {
- name: rawFolder.name
- id: rawFolder._id
- type: "folder"
- children: []
- selected: (rawFolder._id == @selected_entity_id)
- }
- for doc in rawFolder.docs or []
- folder.children.push {
- name: doc.name
- id: doc._id
- type: "doc"
- selected: (doc._id == @selected_entity_id)
- }
- for file in rawFolder.fileRefs or []
- folder.children.push {
- name: file.name
- id: file._id
- type: "file"
- selected: (file._id == @selected_entity_id)
- linkedFileData: file.linkedFileData
- created: file.created
- }
- for childFolder in rawFolder.folders or []
- folder.children.push @_parseFolder(childFolder)
- return folder
- loadDeletedDocs: () ->
- @$scope.deletedDocs = []
- for doc in @$scope.project.deletedDocs or []
- @$scope.deletedDocs.push {
- name: doc.name
- id: doc._id
- type: "doc"
- deleted: true
- }
- recalculateDocList: () ->
- @$scope.docs = []
- @forEachEntity (entity, parentFolder, path) =>
- if entity.type == "doc" and !entity.deleted
- @$scope.docs.push {
- doc: entity
- path: path
- }
- # Keep list ordered by folders, then name
- @$scope.docs.sort (a,b) ->
- aDepth = (a.path.match(/\//g) || []).length
- bDepth = (b.path.match(/\//g) || []).length
- if aDepth - bDepth != 0
- return -(aDepth - bDepth) # Deeper path == folder first
- else if a.path < b.path
- return -1
- else
- return 1
- getEntityPath: (entity) ->
- @_getEntityPathInFolder @$scope.rootFolder, entity
- _getEntityPathInFolder: (folder, entity) ->
- for child in folder.children or []
- if child == entity
- return entity.name
- else if child.type == "folder"
- path = @_getEntityPathInFolder(child, entity)
- if path?
- return child.name + "/" + path
- return null
- getCurrentFolder: () ->
- # Return the root folder if nothing is selected
- @_getCurrentFolder(@$scope.rootFolder) or @$scope.rootFolder
- _getCurrentFolder: (startFolder = @$scope.rootFolder) ->
- for entity in startFolder.children or []
- # The 'current' folder is either the one selected, or
- # the one containing the selected doc/file
- if entity.selected
- if entity.type == "folder"
- return entity
- else
- return startFolder
- if entity.type == "folder"
- result = @_getCurrentFolder(entity)
- return result if result?
- return null
- projectContainsFolder: () ->
- for entity in @$scope.rootFolder.children
- return true if entity.type == 'folder'
- return false
- existsInThisFolder: (folder, name) ->
- for entity in folder?.children or []
- return true if entity.name is name
- return false
- nameExistsError: (message = "already exists") ->
- nameExists = @ide.$q.defer()
- nameExists.reject({data: message})
- return nameExists.promise
- createDoc: (name, parent_folder = @getCurrentFolder()) ->
- # check if a doc/file/folder already exists with this name
- if @existsInThisFolder parent_folder, name
- return @nameExistsError()
- # We'll wait for the socket.io notification to actually
- # add the doc for us.
- @ide.$http.post "/project/#{@ide.project_id}/doc", {
- name: name,
- parent_folder_id: parent_folder?.id
- _csrf: window.csrfToken
- }
- createFolder: (name, parent_folder = @getCurrentFolder()) ->
- # check if a doc/file/folder already exists with this name
- if @existsInThisFolder parent_folder, name
- return @nameExistsError()
- # We'll wait for the socket.io notification to actually
- # add the folder for us.
- return @ide.$http.post "/project/#{@ide.project_id}/folder", {
- name: name,
- parent_folder_id: parent_folder?.id
- _csrf: window.csrfToken
- }
- createLinkedFile: (name, parent_folder = @getCurrentFolder(), provider, data) ->
- # check if a doc/file/folder already exists with this name
- if @existsInThisFolder parent_folder, name
- return @nameExistsError()
- # We'll wait for the socket.io notification to actually
- # add the file for us.
- return @ide.$http.post "/project/#{@ide.project_id}/linked_file", {
- name: name,
- parent_folder_id: parent_folder?.id
- provider,
- data,
- _csrf: window.csrfToken
- }
- refreshLinkedFile: (file) ->
- parent_folder = @_findParentFolder(file)
- data = file.linkedFileData
- provider = data?.provider
- return if !provider?
- return @ide.$http.post "/project/#{@ide.project_id}/linked_file", {
- name: file.name,
- parent_folder_id: parent_folder?.id
- provider,
- data,
- _csrf: window.csrfToken
- }
- renameEntity: (entity, name, callback = (error) ->) ->
- return if entity.name == name
- return if name.length >= 150
- # check if a doc/file/folder already exists with this name
- parent_folder = @getCurrentFolder()
- if @existsInThisFolder parent_folder, name
- return @nameExistsError()
- entity.renamingToName = name
- @ide.$http.post("/project/#{@ide.project_id}/#{entity.type}/#{entity.id}/rename", {
- name: name,
- _csrf: window.csrfToken
- })
- .then () ->
- entity.name = name
- .finally () ->
- entity.renamingToName = null
- deleteEntity: (entity, callback = (error) ->) ->
- # We'll wait for the socket.io notification to
- # delete from scope.
- return @ide.queuedHttp {
- method: "DELETE"
- url: "/project/#{@ide.project_id}/#{entity.type}/#{entity.id}"
- headers:
- "X-Csrf-Token": window.csrfToken
- }
- moveEntity: (entity, parent_folder, callback = (error) ->) ->
- # Abort move if the folder being moved (entity) has the parent_folder as child
- # since that would break the tree structure.
- return if @_isChildFolder(entity, parent_folder)
- # check if a doc/file/folder already exists with this name
- if @existsInThisFolder parent_folder, entity.name
- return @nameExistsError()
- # Wait for the http response before doing the move
- @ide.queuedHttp.post("/project/#{@ide.project_id}/#{entity.type}/#{entity.id}/move", {
- folder_id: parent_folder.id
- _csrf: window.csrfToken
- }).then () =>
- @_moveEntityInScope(entity, parent_folder)
- _isChildFolder: (parent_folder, child_folder) ->
- parent_path = @getEntityPath(parent_folder) or "" # null if root folder
- child_path = @getEntityPath(child_folder) or "" # null if root folder
- # is parent path the beginning of child path?
- return (child_path.slice(0, parent_path.length) == parent_path)
- _deleteEntityFromScope: (entity, options = { moveToDeleted: true }) ->
- return if !entity?
- parent_folder = null
- @forEachEntity (possible_entity, folder) ->
- if possible_entity == entity
- parent_folder = folder
- if parent_folder?
- index = parent_folder.children.indexOf(entity)
- if index > -1
- parent_folder.children.splice(index, 1)
- if entity.type == "doc" and options.moveToDeleted
- entity.deleted = true
- @$scope.deletedDocs.push entity
- _moveEntityInScope: (entity, parent_folder) ->
- return if entity in parent_folder.children
- @_deleteEntityFromScope(entity, moveToDeleted: false)
- parent_folder.children.push(entity)
|