FileList.coffee 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. class FileList extends Class
  2. constructor: (@site, @inner_path, @is_owner=false) ->
  3. @need_update = true
  4. @error = null
  5. @url_root = "/list/" + @site + "/"
  6. if @inner_path
  7. @inner_path += "/"
  8. @url_root += @inner_path
  9. @log("inited", @url_root)
  10. @item_list = new FileItemList(@inner_path)
  11. @item_list.items = @item_list.items
  12. @menu_create = new Menu()
  13. @select_action = null
  14. @selected = {}
  15. @selected_items_num = 0
  16. @selected_items_size = 0
  17. @selected_optional_empty_num = 0
  18. isSelectedAll: ->
  19. false
  20. update: =>
  21. @item_list.update =>
  22. document.body.classList.add("loaded")
  23. getHref: (inner_path) =>
  24. return "/" + @site + "/" + inner_path
  25. getListHref: (inner_path) =>
  26. return "/list/" + @site + "/" + inner_path
  27. getEditHref: (inner_path, mode=null) =>
  28. href = @url_root + "?file=" + inner_path
  29. if mode
  30. href += "&edit_mode=#{mode}"
  31. return href
  32. checkSelectedItems: =>
  33. @selected_items_num = 0
  34. @selected_items_size = 0
  35. @selected_optional_empty_num = 0
  36. for item in @item_list.items
  37. if @selected[item.inner_path]
  38. @selected_items_num += 1
  39. @selected_items_size += item.size
  40. optional_info = @item_list.getOptionalInfo(item.inner_path)
  41. if optional_info and not optional_info.downloaded_percent > 0
  42. @selected_optional_empty_num += 1
  43. handleMenuCreateClick: =>
  44. @menu_create.items = []
  45. @menu_create.items.push ["File", @handleNewFileClick]
  46. @menu_create.items.push ["Directory", @handleNewDirectoryClick]
  47. @menu_create.toggle()
  48. return false
  49. handleNewFileClick: =>
  50. Page.cmd "wrapperPrompt", "New file name:", (file_name) =>
  51. window.top.location.href = @getEditHref(@inner_path + file_name, "new")
  52. return false
  53. handleNewDirectoryClick: =>
  54. Page.cmd "wrapperPrompt", "New directory name:", (res) =>
  55. alert("directory name #{res}")
  56. return false
  57. handleSelectClick: (e) =>
  58. return false
  59. handleSelectEnd: (e) =>
  60. document.body.removeEventListener('mouseup', @handleSelectEnd)
  61. @select_action = null
  62. handleSelectMousedown: (e) =>
  63. inner_path = e.currentTarget.attributes.inner_path.value
  64. if @selected[inner_path]
  65. delete @selected[inner_path]
  66. @select_action = "deselect"
  67. else
  68. @selected[inner_path] = true
  69. @select_action = "select"
  70. @checkSelectedItems()
  71. document.body.addEventListener('mouseup', @handleSelectEnd)
  72. e.stopPropagation()
  73. Page.projector.scheduleRender()
  74. return false
  75. handleRowMouseenter: (e) =>
  76. if e.buttons and @select_action
  77. inner_path = e.target.attributes.inner_path.value
  78. if @select_action == "select"
  79. @selected[inner_path] = true
  80. else
  81. delete @selected[inner_path]
  82. @checkSelectedItems()
  83. Page.projector.scheduleRender()
  84. return false
  85. handleSelectbarCancel: =>
  86. @selected = {}
  87. @checkSelectedItems()
  88. Page.projector.scheduleRender()
  89. return false
  90. handleSelectbarDelete: (e, remove_optional=false) =>
  91. for inner_path of @selected
  92. optional_info = @item_list.getOptionalInfo(inner_path)
  93. delete @selected[inner_path]
  94. if optional_info and not remove_optional
  95. Page.cmd "optionalFileDelete", inner_path
  96. else
  97. Page.cmd "fileDelete", inner_path
  98. @need_update = true
  99. Page.projector.scheduleRender()
  100. @checkSelectedItems()
  101. return false
  102. handleSelectbarRemoveOptional: (e) =>
  103. return @handleSelectbarDelete(e, true)
  104. renderSelectbar: =>
  105. h("div.selectbar", {classes: {visible: @selected_items_num > 0}}, [
  106. "Selected:",
  107. h("span.info", [
  108. h("span.num", "#{@selected_items_num} files"),
  109. h("span.size", "(#{Text.formatSize(@selected_items_size)})"),
  110. ])
  111. h("div.actions", [
  112. if @selected_optional_empty_num > 0
  113. h("a.action.delete.remove_optional", {href: "#", onclick: @handleSelectbarRemoveOptional}, "Delete and remove optional")
  114. else
  115. h("a.action.delete", {href: "#", onclick: @handleSelectbarDelete}, "Delete")
  116. ])
  117. h("a.cancel.link", {href: "#", onclick: @handleSelectbarCancel}, "Cancel")
  118. ])
  119. renderHead: =>
  120. parent_links = []
  121. inner_path_parent = ""
  122. for parent_dir in @inner_path.split("/")
  123. if not parent_dir
  124. continue
  125. if inner_path_parent
  126. inner_path_parent += "/"
  127. inner_path_parent += "#{parent_dir}"
  128. parent_links.push(
  129. [" / ", h("a", {href: @getListHref(inner_path_parent)}, parent_dir)]
  130. )
  131. return h("div.tr.thead", h("div.td.full",
  132. h("a", {href: @getListHref("")}, "root"),
  133. parent_links
  134. ))
  135. renderItemCheckbox: (item) =>
  136. if not @item_list.hasPermissionDelete(item)
  137. return [" "]
  138. return h("a.checkbox-outer", {
  139. href: "#Select",
  140. onmousedown: @handleSelectMousedown,
  141. onclick: @handleSelectClick,
  142. inner_path: item.inner_path
  143. }, h("span.checkbox"))
  144. renderItem: (item) =>
  145. if item.type == "parent"
  146. href = @url_root.replace(/^(.*)\/.{2,255}?$/, "$1/")
  147. else if item.type == "dir"
  148. href = @url_root + item.name
  149. else
  150. href = @url_root.replace(/^\/list\//, "/") + item.name
  151. inner_path = @inner_path + item.name
  152. href_edit = @getEditHref(inner_path)
  153. is_dir = item.type in ["dir", "parent"]
  154. ext = item.name.split(".").pop()
  155. is_editing = inner_path == Page.file_editor?.inner_path
  156. is_editable = not is_dir and item.size < 1024 * 1024 and ext not in window.BINARY_EXTENSIONS
  157. is_modified = @item_list.isModified(inner_path)
  158. is_added = @item_list.isAdded(inner_path)
  159. optional_info = @item_list.getOptionalInfo(inner_path)
  160. style = ""
  161. title = ""
  162. if optional_info
  163. downloaded_percent = optional_info.downloaded_percent
  164. if not downloaded_percent
  165. downloaded_percent = 0
  166. style += "background: linear-gradient(90deg, #fff6dd, #{downloaded_percent}%, white, #{downloaded_percent}%, white);"
  167. is_added = false
  168. if item.ignored
  169. is_added = false
  170. if is_modified then title += " (modified)"
  171. if is_added then title += " (new)"
  172. if optional_info or item.optional_empty then title += " (optional)"
  173. if item.ignored then title += " (ignored from content.json)"
  174. classes = {
  175. "type-#{item.type}": true, editing: is_editing, nobuttons: not is_editable, selected: @selected[inner_path],
  176. modified: is_modified, added: is_added, ignored: item.ignored, optional: optional_info, optional_empty: item.optional_empty
  177. }
  178. h("div.tr", {key: item.name, classes: classes, style: style, onmouseenter: @handleRowMouseenter, inner_path: inner_path}, [
  179. h("div.td.pre", {title: title},
  180. @renderItemCheckbox(item)
  181. ),
  182. h("div.td.name", h("a.link", {href: href}, item.name))
  183. h("div.td.buttons", if is_editable then h("a.edit", {href: href_edit}, if Page.site_info.settings.own then "Edit" else "View"))
  184. h("div.td.size", if is_dir then "[DIR]" else Text.formatSize(item.size))
  185. ])
  186. renderItems: =>
  187. return [
  188. if @item_list.error and not @item_list.items.length and not @item_list.updating then [
  189. h("div.tr", {key: "error"}, h("div.td.full.error", @item_list.error))
  190. ],
  191. if @inner_path then @renderItem({"name": "..", type: "parent", size: 0})
  192. @item_list.items.map @renderItem
  193. ]
  194. renderFoot: =>
  195. files = (item for item in @item_list.items when item.type not in ["parent", "dir"])
  196. dirs = (item for item in @item_list.items when item.type == "dir")
  197. if files.length
  198. total_size = (item.size for file in files).reduce (a, b) -> a + b
  199. else
  200. total_size = 0
  201. foot_text = "Total: "
  202. foot_text += "#{dirs.length} dir, #{files.length} file in #{Text.formatSize(total_size)}"
  203. return [
  204. if dirs.length or files.length or Page.site_info?.settings?.own
  205. h("div.tr.foot-info.foot", h("div.td.full", [
  206. if @item_list.updating
  207. "Updating file list..."
  208. else
  209. if dirs.length or files.length then foot_text
  210. if Page.site_info?.settings?.own
  211. h("div.create", [
  212. h("a.link", {href: "#Create+new+file", onclick: @handleNewFileClick}, "+ New")
  213. @menu_create.render()
  214. ])
  215. ]))
  216. ]
  217. render: =>
  218. if @need_update
  219. @update()
  220. @need_update = false
  221. if not @item_list.items
  222. return []
  223. return h("div.files", [
  224. @renderSelectbar(),
  225. @renderHead(),
  226. h("div.tbody", @renderItems()),
  227. @renderFoot()
  228. ])
  229. window.FileList = FileList