/* global CloudCmd */ import * as load from '#dom/load'; import * as Files from '#dom/files'; import * as Dialog from '#dom/dialog'; import * as Events from '#dom/events'; import {getExt} from '#common/util'; import * as Storage from '#dom/storage'; import * as RESTful from '#dom/rest'; import * as Images from '#dom/images'; import renameCurrent from './operations/rename-current.js'; import * as CurrentFile from './current-file.js'; import * as DOMTree from './dom-tree.js'; import * as Cmd from './cmd.js'; import * as IO from './io/index.js'; import {uploadDirectory} from './directory.js'; import * as Buffer from './buffer.js'; import {loadRemote as _loadRemote} from './load-remote.js'; import {selectByPattern} from './select-by-pattern.js'; const {assign} = Object; const DOM = { getCurrentDirName, getNotCurrentDirPath, getParentDirPath, loadRemote, loadSocket, promptNewDir, promptNewFile, unselectFiles, getActiveFiles, getCurrentDate, getCurrentSize, loadCurrentSize, loadCurrentHash, setCurrentSize, getCurrentMode, getCurrentOwner, getCurrentData, getRefreshButton, getAllFiles, expandSelection, shrinkSelection, setHistory, getCurrentLink, getFilenames, checkStorageHash, saveDataToStorage, getFM, getPanelPosition, getCSSVar, getPanel, getFiles, showPanel, hidePanel, remove, deleteCurrent, deleteSelected, renameCurrent, scrollIntoViewIfNeeded, scrollByPages, changePanel, getPackerExt, goToDirectory, duplicatePanel, swapPanels, updateCurrentInfo, }; assign(DOM, { ...DOMTree, ...CurrentFile, ...Cmd, }); export const CurrentInfo = {}; DOM.Images = Images; DOM.load = load; DOM.Files = Files; DOM.RESTful = RESTful; DOM.IO = IO; DOM.Storage = Storage; DOM.Dialog = Dialog; DOM.CurrentInfo = CurrentInfo; export default DOM; DOM.uploadDirectory = uploadDirectory; DOM.Buffer = Buffer; DOM.Events = Events; const isString = (a) => typeof a === 'string'; const TabPanel = { 'js-left': null, 'js-right': null, }; export function loadRemote(name, options, callback) { _loadRemote(name, options, callback); return DOM; } export function loadSocket(callback) { DOM.loadRemote('socket', { name: 'io', }, callback); return DOM; } /** * create new folder * */ export async function promptNewDir() { await promptNew('directory'); } /** * create new file * * @typeName * @type */ export async function promptNewFile() { await promptNew('file'); } async function promptNew(typeName) { const {Dialog} = DOM; const dir = DOM.getCurrentDirPath(); const msg = `New ${typeName}` || 'File'; const getName = () => { const name = DOM.getCurrentName(); if (name === '..') return ''; return name; }; const name = getName(); const [cancel, currentName] = await Dialog.prompt(msg, name); if (cancel) return; const path = `${dir}${currentName}`; if (typeName === 'directory') await RESTful.createDirectory(path); else await RESTful.write(path); await CloudCmd.refresh({ currentName, }); } /** * get current directory name */ export function getCurrentDirName() { const href = DOM .getCurrentDirPath() .replace(/\/$/, ''); const substr = href.substr(href, href.lastIndexOf('/')); return href.replace(`${substr}/`, '') || '/'; } /** * get current directory path */ export function getParentDirPath(panel) { const path = DOM.getCurrentDirPath(panel); const dirName = DOM.getCurrentDirName() + '/'; const index = path.lastIndexOf(dirName); if (path !== '/') return path.slice(0, index); return path; } /** * get not current directory path */ export function getNotCurrentDirPath() { const panel = DOM.getPanel({ active: false, }); return DOM.getCurrentDirPath(panel); } /* * unselect all files */ export function unselectFiles(files) { files = files || DOM.getSelectedFiles(); Array .from(files) .forEach(DOM.toggleSelectedFile); } /** * get all selected files or current when none selected * * @currentFile */ export function getActiveFiles() { const current = DOM.getCurrentFile(); const files = DOM.getSelectedFiles(); const name = DOM.getCurrentName(current); if (!files.length && name !== '..') return [current]; return files; } export function getCurrentDate(currentFile) { const current = currentFile || DOM.getCurrentFile(); return DOM.getByDataName('js-date', current).textContent; } /** * get size * @currentFile */ export function getCurrentSize(currentFile) { const current = currentFile || DOM.getCurrentFile(); /* если это папка - возвращаем слово dir вместо размера*/ const size = DOM .getByDataName('js-size', current) .textContent .replace(/^<|>$/g, ''); return size; } /** * get size * @currentFile */ export async function loadCurrentSize(currentFile) { const current = currentFile || DOM.getCurrentFile(); const query = '?size'; const link = DOM.getCurrentPath(current); Images.show.load(); if (name === '..') return; const [, size] = await RESTful.read(link + query); DOM.setCurrentSize(size, current); Images.hide(); return current; } /** * load hash * @callback * @currentFile */ export async function loadCurrentHash(currentFile) { const current = currentFile || DOM.getCurrentFile(); const query = '?hash'; const link = DOM.getCurrentPath(current); const [, data] = await RESTful.read(link + query); return data; } /** * set size * @currentFile */ export function setCurrentSize(size, currentFile) { const current = currentFile || DOM.getCurrentFile(); const sizeElement = DOM.getByDataName('js-size', current); sizeElement.textContent = size; } /** * @currentFile */ export function getCurrentMode(currentFile) { const current = currentFile || DOM.getCurrentFile(); const mode = DOM.getByDataName('js-mode', current); return mode.textContent; } /** * @currentFile */ export function getCurrentOwner(currentFile) { const current = currentFile || DOM.getCurrentFile(); const owner = DOM.getByDataName('js-owner', current); return owner.textContent; } /** * unified way to get current file content * * @param currentFile */ export async function getCurrentData(currentFile) { const {Dialog} = DOM; const Info = DOM.CurrentInfo; const current = currentFile || DOM.getCurrentFile(); const path = DOM.getCurrentPath(current); const isDir = DOM.isCurrentIsDir(current); if (Info.name === '..') { Dialog.alert.noFiles(); return [ Error('No Files'), ]; } if (isDir) return await RESTful.read(path); const [hashNew, hash] = await DOM.checkStorageHash(path); if (!hashNew) return [ Error(`Can't get hash of a file`), ]; if (hash === hashNew) return [null, await Storage.get(`${path}-data`)]; const [e, data] = await RESTful.read(path); if (e) return [ e, null, ]; const ONE_MEGABYTE = 1024 ** 2 * 1024; const {length} = data; if (hash && length < ONE_MEGABYTE) await DOM.saveDataToStorage(path, data, hashNew); return [null, data]; } /** * unified way to get RefreshButton */ export function getRefreshButton(panel = DOM.getPanel()) { return DOM.getByDataName('js-refresh', panel); } export function getAllFiles() { const panel = DOM.getPanel(); const files = DOM.getFiles(panel); const name = DOM.getCurrentName(files[0]); const from = (a) => a === '..' ? 1 : 0; const i = from(name); return Array .from(files) .slice(i); } /** * open dialog with expand selection */ export async function expandSelection() { const msg = 'expand'; const {files} = CurrentInfo; await selectByPattern(msg, files); } /** * open dialog with shrink selection */ export async function shrinkSelection() { const msg = 'shrink'; const {files} = CurrentInfo; await selectByPattern(msg, files); } /** * setting history wrapper */ export function setHistory(data, title, url) { const ret = globalThis.history; const {prefix} = CloudCmd; url = prefix + url; if (ret) history.pushState(data, title, url); return ret; } /** * get link from current (or param) file * * @param currentFile - current file by default */ export function getCurrentLink(currentFile) { const current = currentFile || DOM.getCurrentFile(); const link = DOM.getByTag('a', current); return link[0]; } export function getFilenames(files) { if (!files) throw Error('AllFiles could not be empty'); const first = files[0] || DOM.getCurrentFile(); const name = DOM.getCurrentName(first); const allFiles = Array.from(files); if (name === '..') allFiles.shift(); const names = allFiles.map((current) => { return DOM.getCurrentName(current); }); return names; } /** * check storage hash */ export async function checkStorageHash(name) { const nameHash = `${name}-hash`; if (!isString(name)) throw Error('name should be a string!'); const [loadHash, storeHash] = await Promise.all([ DOM.loadCurrentHash(), Storage.get(nameHash), ]); return [loadHash, storeHash]; } /** * save data to storage * * @param name * @param data * @param hash */ export async function saveDataToStorage(name, data, hash) { const isDir = DOM.isCurrentIsDir(); if (isDir) return; hash = hash || await DOM.loadCurrentHash(); const nameHash = `${name}-hash`; const nameData = `${name}-data`; await Storage.set(nameHash, hash); await Storage.set(nameData, data); return hash; } export function getFM() { const {parentElement} = DOM.getPanel(); return parentElement; } export function getPanelPosition(panel) { panel = panel || DOM.getPanel(); return panel.dataset.name.replace('js-', ''); } export function getCSSVar(name, {body = document.body} = {}) { const bodyStyle = getComputedStyle(body); return bodyStyle.getPropertyValue(`--${name}`); } /** function getting panel active, or passive * @param options = {active: true} */ export function getPanel(options) { let files; let panel; let isLeft; let dataName = 'js-'; const current = DOM.getCurrentFile(); if (!current) { panel = DOM.getByDataName('js-left'); } else { files = current.parentElement; panel = files.parentElement; isLeft = panel.getAttribute('data-name') === 'js-left'; } /* if {active : false} getting passive panel */ if (options && !options.active) { dataName += isLeft ? 'right' : 'left'; panel = DOM.getByDataName(dataName); } /* if two panels showed * then always work with passive * panel */ if (globalThis.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH) panel = DOM.getByDataName('js-left'); if (!panel) throw Error('can not find Active Panel!'); return panel; } export function getFiles(element) { const files = DOM.getByDataName('js-files', element); return files.children || []; } /** * shows panel right or left (or active) */ export function showPanel(active) { const panel = DOM.getPanel({ active, }); if (!panel) return false; DOM.show(panel); return true; } /** * hides panel right or left (or active) */ export function hidePanel(active) { const panel = DOM.getPanel({ active, }); if (!panel) return false; return DOM.hide(panel); } /** * remove child of element * @param child * @param element */ export function remove(child, element) { const parent = element || document.body; parent.removeChild(child); return DOM; } /** * remove current file from file table * @param current * */ export function deleteCurrent(current) { if (!current) DOM.getCurrentFile(); const parent = current?.parentElement; const name = DOM.getCurrentName(current); if (current && name !== '..') { const next = current.nextSibling; const prev = current.previousSibling; DOM.setCurrentFile(next || prev); parent.removeChild(current); } } /** * remove selected files from file table * @Selected */ export function deleteSelected(selected) { selected = selected || DOM.getSelectedFiles(); if (!selected) return; selected.map(DOM.deleteCurrent); } /** * rename current file * * @currentFile */ export function scrollIntoViewIfNeeded(element, center = false) { if (!element || !element.scrollIntoViewIfNeeded) return; element.scrollIntoViewIfNeeded(center); } /* scroll on one page */ export function scrollByPages(element, pPages) { const ret = element?.scrollByPages && pPages; if (ret) element.scrollByPages(pPages); return ret; } export function changePanel() { const Info = CurrentInfo; let panel = DOM.getPanel(); CloudCmd.emit('passive-dir', Info.dirPath); const panelPassive = DOM.getPanel({ active: false, }); let name = DOM.getCurrentName(); const filesPassive = DOM.getFiles(panelPassive); let dataName = panel.getAttribute('data-name'); TabPanel[dataName] = name; panel = panelPassive; dataName = panel.getAttribute('data-name'); name = TabPanel[dataName]; let files; let current; if (name) { current = DOM.getCurrentByName(name, panel); if (current) files = current.parentElement; } if (!files || !files.parentElement) { current = DOM.getCurrentByName(name, panel); if (!current) [current] = filesPassive; } DOM.setCurrentFile(current, { history: true, }); CloudCmd.emit('active-dir', Info.dirPath); return DOM; } export function getPackerExt(type) { if (type === 'zip') return '.zip'; return '.tar.gz'; } export async function goToDirectory(overrides = {}) { const {Dialog} = DOM; const { prompt = Dialog.prompt, changeDir = CloudCmd.changeDir, } = overrides; const msg = 'Go to directory:'; const {dirPath} = CurrentInfo; const [cancel, path = dirPath] = await prompt(msg, dirPath); if (cancel) return; await changeDir(path); } export async function duplicatePanel() { const Info = CurrentInfo; const {isDir} = Info; const panel = Info.panelPassive; const noCurrent = !Info.isOnePanel; const getPath = (isDir) => { if (isDir) return Info.path; return Info.dirPath; }; const path = getPath(isDir); await CloudCmd.changeDir(path, { panel, noCurrent, }); } export async function swapPanels() { const Info = CurrentInfo; const { panel, files, element, panelPassive, } = Info; const path = DOM.getCurrentDirPath(); const dirPathPassive = DOM.getNotCurrentDirPath(); let currentIndex = files.indexOf(element); await CloudCmd.changeDir(path, { panel: panelPassive, noCurrent: true, }); await CloudCmd.changeDir(dirPathPassive, { panel, }); const length = Info.files.length - 1; if (currentIndex > length) currentIndex = length; const el = Info.files[currentIndex]; DOM.setCurrentFile(el); } export function updateCurrentInfo(currentFile) { const info = DOM.CurrentInfo; const current = currentFile || DOM.getCurrentFile(); const files = current.parentElement; const panelPassive = DOM.getPanel({ active: false, }); const filesPassive = DOM.getFiles(panelPassive); const name = DOM.getCurrentName(current); info.dir = DOM.getCurrentDirName(); info.dirPath = DOM.getCurrentDirPath(); info.parentDirPath = DOM.getParentDirPath(); info.element = current; info.ext = getExt(name); info.files = Array.from(files.children); info.filesPassive = Array.from(filesPassive); info.first = files.firstChild; info.getData = DOM.getCurrentData; info.last = files.lastChild; info.link = DOM.getCurrentLink(current); info.mode = DOM.getCurrentMode(current); info.name = name; info.path = DOM.getCurrentPath(current); info.panel = files.parentElement || DOM.getPanel(); info.panelPassive = panelPassive; info.size = DOM.getCurrentSize(current); info.isDir = DOM.isCurrentIsDir(); info.isSelected = DOM.isSelected(current); info.panelPosition = DOM .getPanel() .dataset .name .replace('js-', ''); info.isOnePanel = info.panel.getAttribute('data-name') === info.panelPassive.getAttribute('data-name'); }