BinaryFileController.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import App from '../../../base'
  2. export default App.controller('BinaryFileController', function(
  3. $scope,
  4. $rootScope,
  5. $http,
  6. $timeout,
  7. $element,
  8. ide,
  9. waitFor
  10. ) {
  11. const MAX_FILE_SIZE = 2 * 1024 * 1024
  12. const MAX_URL_LENGTH = 60
  13. const FRONT_OF_URL_LENGTH = 35
  14. const FILLER = '...'
  15. const TAIL_OF_URL_LENGTH =
  16. MAX_URL_LENGTH - FRONT_OF_URL_LENGTH - FILLER.length
  17. const textExtensions = window.ExposedSettings.textExtensions
  18. const imageExtensions = ['png', 'jpg', 'jpeg', 'gif']
  19. const previewableExtensions = []
  20. const extension = file =>
  21. file.name
  22. .split('.')
  23. .pop()
  24. .toLowerCase()
  25. $scope.isTextFile = () =>
  26. textExtensions.indexOf(extension($scope.openFile)) > -1
  27. $scope.isImageFile = () =>
  28. imageExtensions.indexOf(extension($scope.openFile)) > -1
  29. $scope.isPreviewableFile = () =>
  30. previewableExtensions.indexOf(extension($scope.openFile)) > -1
  31. $scope.isUnpreviewableFile = () =>
  32. !$scope.isTextFile() && !$scope.isImageFile() && !$scope.isPreviewableFile()
  33. $scope.textPreview = {
  34. loading: false,
  35. shouldShowDots: false,
  36. error: false,
  37. data: null
  38. }
  39. $scope.refreshing = false
  40. $scope.refreshError = null
  41. $scope.displayUrl = function(url) {
  42. if (url == null) {
  43. return
  44. }
  45. if (url.length > MAX_URL_LENGTH) {
  46. const front = url.slice(0, FRONT_OF_URL_LENGTH)
  47. const tail = url.slice(url.length - TAIL_OF_URL_LENGTH)
  48. return front + FILLER + tail
  49. }
  50. return url
  51. }
  52. $scope.refreshFile = function(file) {
  53. $scope.refreshing = true
  54. $scope.refreshError = null
  55. window.expectingLinkedFileRefreshedSocketFor = file.name
  56. ide.fileTreeManager
  57. .refreshLinkedFile(file)
  58. .then(function(response) {
  59. const { data } = response
  60. const newFileId = data.new_file_id
  61. $timeout(
  62. () =>
  63. waitFor(() => ide.fileTreeManager.findEntityById(newFileId), 5000)
  64. .then(newFile => {
  65. ide.binaryFilesManager.openFile(newFile)
  66. window.expectingLinkedFileRefreshedSocketFor = null
  67. })
  68. .catch(err => console.warn(err)),
  69. 0
  70. )
  71. $scope.refreshError = null
  72. })
  73. .catch(response => ($scope.refreshError = response.data))
  74. .finally(() => {
  75. $scope.refreshing = false
  76. const provider = file.linkedFileData.provider
  77. if (
  78. provider === 'mendeley' ||
  79. provider === 'zotero' ||
  80. file.name.match(/^.*\.bib$/)
  81. ) {
  82. ide.$scope.$emit('references:should-reindex', {})
  83. }
  84. })
  85. }
  86. // Callback fired when the `img` tag fails to load,
  87. // `failedLoad` used to show the "No Preview" message
  88. $scope.failedLoad = false
  89. window.sl_binaryFilePreviewError = () => {
  90. $scope.failedLoad = true
  91. $scope.$apply()
  92. }
  93. // Callback fired when the `img` tag is done loading,
  94. // `imgLoaded` is used to show the spinner gif while loading
  95. $scope.imgLoaded = false
  96. window.sl_binaryFilePreviewLoaded = () => {
  97. $scope.imgLoaded = true
  98. $scope.$apply()
  99. }
  100. if ($scope.isTextFile()) {
  101. loadTextFilePreview()
  102. }
  103. function loadTextFilePreview() {
  104. const url = `/project/${window.project_id}/file/${$scope.openFile.id}`
  105. let truncated = false
  106. displayPreviewLoading()
  107. getFileSize(url)
  108. .then(fileSize => {
  109. const opts = {}
  110. if (fileSize > MAX_FILE_SIZE) {
  111. truncated = true
  112. opts.maxSize = MAX_FILE_SIZE
  113. }
  114. return getFileContents(url, opts)
  115. })
  116. .then(contents => {
  117. const displayedContents = truncated
  118. ? truncateFileContents(contents)
  119. : contents
  120. displayPreview(displayedContents, truncated)
  121. })
  122. .catch(err => {
  123. console.error(err)
  124. displayPreviewError()
  125. })
  126. }
  127. function getFileSize(url) {
  128. return $http.head(url).then(response => {
  129. const size = parseInt(response.headers('Content-Length'), 10)
  130. if (isNaN(size)) {
  131. throw new Error('Could not parse Content-Length header')
  132. }
  133. return size
  134. })
  135. }
  136. function getFileContents(url, opts = {}) {
  137. const { maxSize } = opts
  138. if (maxSize != null) {
  139. url += `?range=0-${maxSize}`
  140. }
  141. return $http
  142. .get(url, {
  143. transformResponse: null // Don't parse JSON
  144. })
  145. .then(response => {
  146. return response.data
  147. })
  148. }
  149. function truncateFileContents(contents) {
  150. return contents.replace(/\n.*$/, '')
  151. }
  152. function displayPreviewLoading() {
  153. $scope.textPreview.data = null
  154. $scope.textPreview.loading = true
  155. $scope.textPreview.shouldShowDots = false
  156. $scope.$apply()
  157. }
  158. function displayPreview(contents, truncated) {
  159. $scope.textPreview.error = false
  160. $scope.textPreview.data = contents
  161. $scope.textPreview.shouldShowDots = truncated
  162. $timeout(setPreviewHeight, 0)
  163. }
  164. function displayPreviewError() {
  165. $scope.textPreview.error = true
  166. $scope.textPreview.loading = false
  167. }
  168. function setPreviewHeight() {
  169. const $preview = $element.find('.text-preview .scroll-container')
  170. const $header = $element.find('.binary-file-header')
  171. const maxHeight = $element.height() - $header.height() - 14 // borders + margin
  172. $preview.css({ 'max-height': maxHeight })
  173. // Don't show the preview until we've set the height, otherwise we jump around
  174. $scope.textPreview.loading = false
  175. }
  176. })