ide.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /* eslint-disable
  2. camelcase,
  3. max-len,
  4. no-cond-assign,
  5. no-return-assign,
  6. no-undef,
  7. no-unused-vars,
  8. no-useless-escape,
  9. */
  10. // TODO: This file was created by bulk-decaffeinate.
  11. // Fix any style issues and re-enable lint.
  12. /*
  13. * decaffeinate suggestions:
  14. * DS101: Remove unnecessary use of Array.from
  15. * DS102: Remove unnecessary code created because of implicit returns
  16. * DS103: Rewrite code to no longer use __guard__
  17. * DS207: Consider shorter variations of null checks
  18. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  19. */
  20. define([
  21. 'base',
  22. 'ide/file-tree/FileTreeManager',
  23. 'ide/connection/ConnectionManager',
  24. 'ide/editor/EditorManager',
  25. 'ide/online-users/OnlineUsersManager',
  26. 'ide/history/HistoryManager',
  27. 'ide/history/HistoryV2Manager',
  28. 'ide/permissions/PermissionsManager',
  29. 'ide/pdf/PdfManager',
  30. 'ide/binary-files/BinaryFilesManager',
  31. 'ide/references/ReferencesManager',
  32. 'ide/metadata/MetadataManager',
  33. 'ide/review-panel/ReviewPanelManager',
  34. 'ide/SafariScrollPatcher',
  35. 'ide/cobranding/CobrandingDataService',
  36. 'ide/settings/index',
  37. 'ide/share/index',
  38. 'ide/chat/index',
  39. 'ide/clone/index',
  40. 'ide/hotkeys/index',
  41. 'ide/wordcount/index',
  42. 'ide/directives/layout',
  43. 'ide/directives/validFile',
  44. 'ide/services/ide',
  45. 'analytics/AbTestingManager',
  46. 'directives/focus',
  47. 'directives/fineUpload',
  48. 'directives/scroll',
  49. 'directives/onEnter',
  50. 'directives/stopPropagation',
  51. 'directives/rightClick',
  52. 'directives/expandableTextArea',
  53. 'directives/videoPlayState',
  54. 'services/queued-http',
  55. 'services/validateCaptcha',
  56. 'services/validateCaptchaV3',
  57. 'services/wait-for',
  58. 'filters/formatDate',
  59. 'main/event',
  60. 'main/account-upgrade',
  61. 'main/exposed-settings',
  62. 'main/system-messages',
  63. '../../modules/modules-ide.js'
  64. ], function(
  65. App,
  66. FileTreeManager,
  67. ConnectionManager,
  68. EditorManager,
  69. OnlineUsersManager,
  70. HistoryManager,
  71. HistoryV2Manager,
  72. PermissionsManager,
  73. PdfManager,
  74. BinaryFilesManager,
  75. ReferencesManager,
  76. MetadataManager,
  77. ReviewPanelManager,
  78. SafariScrollPatcher
  79. ) {
  80. App.controller('IdeController', function(
  81. $scope,
  82. $timeout,
  83. ide,
  84. localStorage,
  85. eventTracking,
  86. metadata,
  87. $q,
  88. CobrandingDataService
  89. ) {
  90. // Don't freak out if we're already in an apply callback
  91. let err, pdfLayout, userAgent
  92. $scope.$originalApply = $scope.$apply
  93. $scope.$apply = function(fn) {
  94. if (fn == null) {
  95. fn = function() {}
  96. }
  97. const phase = this.$root.$$phase
  98. if (phase === '$apply' || phase === '$digest') {
  99. return fn()
  100. } else {
  101. return this.$originalApply(fn)
  102. }
  103. }
  104. $scope.state = {
  105. loading: true,
  106. load_progress: 40,
  107. error: null
  108. }
  109. $scope.ui = {
  110. leftMenuShown: false,
  111. view: 'editor',
  112. chatOpen: false,
  113. pdfLayout: 'sideBySide',
  114. pdfHidden: false,
  115. pdfWidth: 0,
  116. reviewPanelOpen: localStorage(`ui.reviewPanelOpen.${window.project_id}`),
  117. miniReviewPanelVisible: false,
  118. chatResizerSizeOpen: window.uiConfig.chatResizerSizeOpen,
  119. chatResizerSizeClosed: window.uiConfig.chatResizerSizeClosed,
  120. defaultFontFamily: window.uiConfig.defaultFontFamily,
  121. defaultLineHeight: window.uiConfig.defaultLineHeight
  122. }
  123. $scope.user = window.user
  124. $scope.settings = window.userSettings
  125. $scope.anonymous = window.anonymous
  126. $scope.isTokenMember = window.isTokenMember
  127. $scope.isRestrictedTokenMember = window.isRestrictedTokenMember
  128. $scope.cobranding = {
  129. isProjectCobranded: CobrandingDataService.isProjectCobranded(),
  130. logoImgUrl: CobrandingDataService.getLogoImgUrl(),
  131. submitBtnHtml: CobrandingDataService.getSubmitBtnHtml(),
  132. brandVariationName: CobrandingDataService.getBrandVariationName(),
  133. brandVariationHomeUrl: CobrandingDataService.getBrandVariationHomeUrl()
  134. }
  135. $scope.chat = {}
  136. ide.toggleReviewPanel = $scope.toggleReviewPanel = function() {
  137. if (!$scope.project.features.trackChangesVisible) {
  138. return
  139. }
  140. $scope.ui.reviewPanelOpen = !$scope.ui.reviewPanelOpen
  141. return eventTracking.sendMB('rp-toggle-panel', {
  142. value: $scope.ui.reviewPanelOpen
  143. })
  144. }
  145. $scope.$watch('ui.reviewPanelOpen', function(value) {
  146. if (value != null) {
  147. return localStorage(`ui.reviewPanelOpen.${window.project_id}`, value)
  148. }
  149. })
  150. $scope.$on('layout:pdf:resize', function(_, layoutState) {
  151. $scope.ui.pdfHidden = layoutState.east.initClosed
  152. return ($scope.ui.pdfWidth = layoutState.east.size)
  153. })
  154. $scope.$watch('ui.view', function(newView, oldView) {
  155. if (newView !== oldView) {
  156. $scope.$broadcast('layout:flat-screen:toggle')
  157. }
  158. if (newView != null && newView !== 'editor' && newView !== 'pdf') {
  159. return eventTracking.sendMBOnce(`ide-open-view-${newView}-once`)
  160. }
  161. })
  162. $scope.$watch('ui.chatOpen', function(isOpen) {
  163. if (isOpen) {
  164. return eventTracking.sendMBOnce('ide-open-chat-once')
  165. }
  166. })
  167. $scope.$watch('ui.leftMenuShown', function(isOpen) {
  168. if (isOpen) {
  169. return eventTracking.sendMBOnce('ide-open-left-menu-once')
  170. }
  171. })
  172. $scope.trackHover = feature =>
  173. eventTracking.sendMBOnce(`ide-hover-${feature}-once`)
  174. // End of tracking code.
  175. window._ide = ide
  176. ide.validFileRegex = '^[^*/]*$' // Don't allow * and /
  177. let useFallbackWebsocket =
  178. window.location &&
  179. window.location.search &&
  180. window.location.search.match(/ws=fallback/)
  181. // if we previously failed to load the websocket fall back to null (the siteUrl)
  182. ide.wsUrl = useFallbackWebsocket ? null : window.sharelatex.wsUrl || null // websocket url (if defined)
  183. ide.project_id = $scope.project_id = window.project_id
  184. ide.$scope = $scope
  185. ide.referencesSearchManager = new ReferencesManager(ide, $scope)
  186. ide.connectionManager = new ConnectionManager(ide, $scope)
  187. ide.fileTreeManager = new FileTreeManager(ide, $scope)
  188. ide.editorManager = new EditorManager(ide, $scope, localStorage)
  189. ide.onlineUsersManager = new OnlineUsersManager(ide, $scope)
  190. if (window.data.useV2History) {
  191. ide.historyManager = new HistoryV2Manager(ide, $scope, localStorage)
  192. } else {
  193. ide.historyManager = new HistoryManager(ide, $scope)
  194. }
  195. ide.pdfManager = new PdfManager(ide, $scope)
  196. ide.permissionsManager = new PermissionsManager(ide, $scope)
  197. ide.binaryFilesManager = new BinaryFilesManager(ide, $scope)
  198. ide.metadataManager = new MetadataManager(ide, $scope, metadata)
  199. let inited = false
  200. $scope.$on('project:joined', function() {
  201. if (inited) {
  202. return
  203. }
  204. inited = true
  205. if (
  206. __guard__(
  207. $scope != null ? $scope.project : undefined,
  208. x => x.deletedByExternalDataSource
  209. )
  210. ) {
  211. ide.showGenericMessageModal(
  212. 'Project Renamed or Deleted',
  213. `\
  214. This project has either been renamed or deleted by an external data source such as Dropbox.
  215. We don't want to delete your data on Overleaf, so this project still contains your history and collaborators.
  216. If the project has been renamed please look in your project list for a new project under the new name.\
  217. `
  218. )
  219. }
  220. return $timeout(function() {
  221. if ($scope.permissions.write) {
  222. let _labelsInitialLoadDone
  223. ide.metadataManager.loadProjectMetaFromServer()
  224. return (_labelsInitialLoadDone = true)
  225. }
  226. }, 200)
  227. })
  228. // Count the first 'doc:opened' as a sign that the ide is loaded
  229. // and broadcast a message. This is a good event to listen for
  230. // if you want to wait until the ide is fully loaded and initialized
  231. let _loaded = false
  232. $scope.$on('doc:opened', function() {
  233. if (_loaded) {
  234. return
  235. }
  236. $scope.$broadcast('ide:loaded')
  237. return (_loaded = true)
  238. })
  239. $scope.$on('cursor:editor:update', eventTracking.editingSessionHeartbeat)
  240. const DARK_THEMES = [
  241. 'ambiance',
  242. 'chaos',
  243. 'clouds_midnight',
  244. 'cobalt',
  245. 'idle_fingers',
  246. 'merbivore',
  247. 'merbivore_soft',
  248. 'mono_industrial',
  249. 'monokai',
  250. 'pastel_on_dark',
  251. 'solarized_dark',
  252. 'terminal',
  253. 'tomorrow_night',
  254. 'tomorrow_night_blue',
  255. 'tomorrow_night_bright',
  256. 'tomorrow_night_eighties',
  257. 'twilight',
  258. 'vibrant_ink'
  259. ]
  260. $scope.darkTheme = false
  261. $scope.$watch('settings.editorTheme', function(theme) {
  262. if (Array.from(DARK_THEMES).includes(theme)) {
  263. return ($scope.darkTheme = true)
  264. } else {
  265. return ($scope.darkTheme = false)
  266. }
  267. })
  268. ide.localStorage = localStorage
  269. ide.browserIsSafari = false
  270. $scope.switchToFlatLayout = function(view) {
  271. $scope.ui.pdfLayout = 'flat'
  272. $scope.ui.view = view
  273. return ide.localStorage('pdf.layout', 'flat')
  274. }
  275. $scope.switchToSideBySideLayout = function(view) {
  276. $scope.ui.pdfLayout = 'sideBySide'
  277. $scope.ui.view = view
  278. return localStorage('pdf.layout', 'split')
  279. }
  280. if ((pdfLayout = localStorage('pdf.layout'))) {
  281. if (pdfLayout === 'split') {
  282. $scope.switchToSideBySideLayout()
  283. }
  284. if (pdfLayout === 'flat') {
  285. $scope.switchToFlatLayout()
  286. }
  287. } else {
  288. $scope.switchToSideBySideLayout()
  289. }
  290. try {
  291. ;({ userAgent } = navigator)
  292. ide.browserIsSafari =
  293. userAgent &&
  294. /.*Safari\/.*/.test(userAgent) &&
  295. !/.*Chrome\/.*/.test(userAgent) &&
  296. !/.*Chromium\/.*/.test(userAgent)
  297. } catch (error) {
  298. err = error
  299. console.error(err)
  300. }
  301. if (ide.browserIsSafari) {
  302. ide.safariScrollPatcher = new SafariScrollPatcher($scope)
  303. }
  304. // Fix Chrome 61 and 62 text-shadow rendering
  305. let browserIsChrome61or62 = false
  306. try {
  307. const chromeVersion =
  308. parseFloat(navigator.userAgent.split(' Chrome/')[1]) || null
  309. browserIsChrome61or62 = chromeVersion != null
  310. if (browserIsChrome61or62) {
  311. document.styleSheets[0].insertRule(
  312. '.ace_editor.ace_autocomplete .ace_completion-highlight { text-shadow: none !important; font-weight: bold; }',
  313. 1
  314. )
  315. }
  316. } catch (error1) {
  317. err = error1
  318. console.error(err)
  319. }
  320. // User can append ?ft=somefeature to url to activate a feature toggle
  321. ide.featureToggle = __guard__(
  322. __guard__(
  323. typeof location !== 'undefined' && location !== null
  324. ? location.search
  325. : undefined,
  326. x1 => x1.match(/^\?ft=(\w+)$/)
  327. ),
  328. x => x[1]
  329. )
  330. return ide.socket.on('project:publicAccessLevel:changed', data => {
  331. if (data.newAccessLevel != null) {
  332. ide.$scope.project.publicAccesLevel = data.newAccessLevel
  333. return $scope.$digest()
  334. }
  335. })
  336. })
  337. return angular.bootstrap(document.body, ['SharelatexApp'])
  338. })
  339. function __guard__(value, transform) {
  340. return typeof value !== 'undefined' && value !== null
  341. ? transform(value)
  342. : undefined
  343. }