state.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /*
  2. state.js
  3. All module-level state for the File Manager.
  4. Part of the ArozOS File Manager. Loaded as a plain script from
  5. file_explorer.html - see the <script> block at the end of that file.
  6. */
  7. let directorySidebarWidth = 250; //Width of the sidebar
  8. //Which special view the sidebar context menu was opened on, if any
  9. let contextMenuSpecialView = "";
  10. /*
  11. Trash bin layout thresholds, measured on the file area rather than on the
  12. window: both panes beside it are draggable, so the viewport says little
  13. about how much room the table actually has.
  14. */
  15. const FM_TRASH_ORIGIN_MIN_WIDTH = 900; //Below this the original path column goes
  16. const FM_TRASH_CARD_MAX_WIDTH = 560; //Below this the table becomes cards
  17. /*
  18. Pane splitter limits (see js/explorer/splitter.js)
  19. The quick access sidebar opens at its widest and is only ever dragged
  20. narrower, so its default and its maximum are the same number. Its floor is
  21. expressed as a fraction of that maximum rather than as its own pixel value,
  22. so changing the maximum keeps the two in proportion.
  23. The properties pane is free to go either way between its own two bounds.
  24. FM_MAIN_MIN_WIDTH is what stops either drag from squeezing the file list
  25. itself down to nothing on a narrow window.
  26. */
  27. const FM_SIDEBAR_MAX_WIDTH = 250; //also the default width
  28. const FM_SIDEBAR_MIN_RATIO = 1 / 3; //narrowest the sidebar may be dragged
  29. const FM_PROPS_DEFAULT_WIDTH = 300;
  30. const FM_PROPS_MIN_WIDTH = 220;
  31. const FM_PROPS_MAX_WIDTH = 620;
  32. const FM_MAIN_MIN_WIDTH = 320; //the file list never shrinks past this
  33. let sideBarShown = true; //Indicate if sidebar is shown
  34. let currentTheme = "whiteTheme"; //Default theme
  35. let viewMode = "list"; //Viewmode, support {list, grid, detail}
  36. let sortMode = "default"; //Sortmode, support {default, reverse, smallToLarge, largeToSmall, mostRecent, leastRecent}
  37. let currentPath = "user:/"
  38. let showOprBar = true; //Classic file operation toolbar, persisted server side
  39. let showHiddenFiles = false; //Dotfiles and .metadata folders, persisted server side
  40. let gridZoom = 170; //Grid tile width in px (100-170), driven by the status bar slider
  41. let viewHistory = [];
  42. let forwardHistory = []; //Paths popped by Back, replayed by Forward. Cleared on any other navigation. //View history
  43. let currentFilelist = []; //The current file list in the currentPath
  44. let currentPathHash = ""; //The folder content hash
  45. let enableAutoRefresh = true; //Enable directory updates on file change
  46. let filesIconTheme = "default";
  47. //File operations
  48. let clipboard = []; //System clipboard
  49. let cutMode = false; //If set to true, this is cut mode. Else copy mode
  50. let useLocalstorage = lscheck();
  51. let overwriteMode = "keep"; //Overwrite mode, support {skip, overwrite, keep}
  52. let thumbLoader = null; //Handle from FileThumb.loadThumbnails, cancelled on navigation
  53. let pathInputMode = false;
  54. let renameMode = false;
  55. //Searching related
  56. let searchCaseSensitive = false;
  57. let viewModeBeforeSearch = "list";
  58. let searchMode = false;
  59. let hotSearchBuffer = "";
  60. let hotSearchTimer = null;
  61. let hotSearchOffsetIndex = 0;
  62. let propertiesView = false; //Enable viewing properties on the right sidebar
  63. //Keypress listeners
  64. let stickyMultiSelect = false; //Mobile multi-select stays on until the user turns it off
  65. let ctrlHold = false;
  66. let shiftHold = false;
  67. let lastClickedElement = undefined;
  68. /*
  69. Mobile double tap tracking.
  70. Touch browsers do not fire a reliable dblclick on these rows, so the gesture
  71. is timed here instead. Kept per file id so two quick taps on different items
  72. are never mistaken for a double tap on one.
  73. */
  74. let lastTapFileID = null;
  75. let lastTapTime = 0;
  76. const MOBILE_DOUBLE_TAP_MS = 350;
  77. //Upload related
  78. let uploadingFileCount = 0;
  79. let maxConcurrentUpload = 4; //Maxmium number of oncurrent upload
  80. let uploadPendingList = []; //Upload pending queue for mass upoad
  81. let uploadRetryMap = new Map(); //taskUUID -> {file, targetDir} for WebSocket upload retry
  82. let lowMemoryMode = true; //Upload with low memory mode channel
  83. let largeFileCutoffSize = 8192 * 1024 * 1024; //Any file larger than this size is consider "large file", default to 8GB
  84. let uploadFileChunkSize = 1024 * 1024 * 1; //1MB, 4MB not working quite well on slow network
  85. let uploadWindowSize = 8 * 1024 * 1024; //Bytes of chunks kept in flight per upload connection (see script/chunkupload.js)
  86. let postUploadModeCutoff = 25 * 1048576; //25MB, files smaller than this will upload using POST Mode
  87. const CHUNK_TIMEOUT_MS = 30000; //30s without any server "next" ack (once everything queued has been sent) before resending the oldest chunk
  88. /*
  89. Heartbeat interval while an upload is paused. Comfortably under the 60s idle
  90. timeout most reverse proxies default to, so a pause left running overnight
  91. does not quietly lose its connection.
  92. The matching server side limits are uploadPauseTimeout and
  93. uploadPauseCloseCode in src/file_system.go - a pause left longer than the
  94. server's timeout is cancelled there and closed with that code.
  95. */
  96. const UPLOAD_PAUSE_PING_MS = 20000;
  97. const UPLOAD_PAUSE_TIMEOUT_CLOSE_CODE = 4001;
  98. const MAX_CHUNK_RETRIES = 3; //Maximum retries without any progress before failing the upload
  99. /*
  100. Transfer panel state (see js/explorer/uploadui.js)
  101. uploadTaskInfo holds everything the panel needs to draw a row that the DOM
  102. cannot cheaply answer for us - byte counts, the running speed estimate the
  103. "time left" label is derived from, and the task state. Keyed by task UUID.
  104. uploadTransferMap holds the live transfer handles so the per-row button and
  105. "Cancel all" can actually stop a transfer: {abort, pause, resume, pausable}.
  106. Only tasks currently in flight have an entry.
  107. */
  108. let uploadTaskInfo = new Map();
  109. let uploadTransferMap = new Map();
  110. //Panel collapsed to the #fmUploadListBtn in the status bar; a new task expands it
  111. let uploadPanelCollapsed = false;
  112. //Panel minimized to the compact card (ring + total only) in the same corner
  113. let uploadPanelCompact = false;
  114. const MAX_FINISHED_UPLOAD_ROWS = 60; //Trim completed rows past this to keep mass uploads responsive
  115. /*
  116. The task list follows the transfer that is currently in flight, so a long
  117. queue does not leave the user staring at finished rows. Scrolling by hand
  118. wins for a while - yanking the list back under someone who is reading it is
  119. worse than letting the active row drift off screen.
  120. */
  121. let uploadListUserScrolledAt = 0;
  122. /*
  123. The scroll offset this code last set itself, so the scroll handler can tell
  124. its own scrolling from the user's. Deliberately a position rather than a
  125. "the next event is mine" flag: scrollTop can be assigned without producing
  126. any movement (it is already clamped at the end of the list), which fires no
  127. event and would leave such a flag stuck - swallowing the user's next scroll.
  128. */
  129. let uploadListAutoScrollTop = -1;
  130. const UPLOAD_FOLLOW_PAUSE_MS = 5000; //How long a manual scroll suspends following
  131. //Pending auto-dismiss for the toast, so a new message replaces the old timer
  132. let msgboxTimer = null;
  133. //Deferred second layout pass after a resize / rotation (see applyResponsiveLayout)
  134. let responsiveLayoutTimer = null;
  135. //Module-registered extension icons (ext without dot → web-root-relative path)
  136. var extIconRegistry = {};
  137. //File Sharing related
  138. let shareEditingObject = "";
  139. //System Information
  140. //Browser detection
  141. let isMobile = window.mobilecheck();
  142. let isChromium = window.chrome;
  143. let isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
  144. let isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
  145. let isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
  146. //Bind onclicke events for fileObjects
  147. var lastClickedFileID = 0;