var remote_home_dir; var svg_add_folder = ; var svg_trash = ; var svg_arrow = ; var svg_home = ; var svg_refresh = ; var svg_cancel = ; var svg_continue = ; var svg_computer = ; function getSize(type, size) { if (!size) { if (type <= 3) return ""; return "0B"; } size = size.toFloat(); var toFixed = function(size) { size = (size * 100).toInteger(); var a = (size / 100).toInteger(); if (size % 100 == 0) return a; if (size % 10 == 0) return a + '.' + (size % 10); var b = size % 100; if (b < 10) b = '0' + b; return a + '.' + b; } if (size < 1024) return size.toInteger() + "B"; if (size < 1024 * 1024) return toFixed(size / 1024) + "K"; if (size < 1024 * 1024 * 1024) return toFixed(size / (1024 * 1024)) + "M"; return toFixed(size / (1024 * 1024 * 1024)) + "G"; } function getParentPath(is_remote, path) { var sep = handler.get_path_sep(is_remote); var res = path.lastIndexOf(sep); if (res <= 0) return "/"; return path.substr(0, res); } function getFileName(is_remote, path) { var sep = handler.get_path_sep(is_remote); var res = path.lastIndexOf(sep); return path.substr(res + 1); } function getExt(name) { if (name.indexOf(".") == 0) { return ""; } var i = name.lastIndexOf("."); if (i > 0) return name.substr(i + 1); return ""; } var jobIdCounter = 1; class JobTable: Reactor.Component { this var jobs = []; this var job_map = {}; function render() { var me = this; var rows = this.jobs.map(function(job, i) { return me.renderRow(job, i); }); return
{rows}
; } event click $(svg.cancel) (_, me) { var job = this.jobs[me.parent.parent.index]; var id = job.id; handler.cancel_job(id); delete this.job_map[id]; var i = -1; this.jobs.map(function(job, idx) { if (job.id == id) i = idx; }); this.jobs.splice(i, 1); this.update(); var is_remote = job.is_remote; if (job.type != "del-dir") is_remote = !is_remote; refreshDir(is_remote); } event click $(svg.continue) (_, me) { var job = this.jobs[me.parent.parent.parent.index]; var id = job.id; this.continueJob(id); this.update(); } function clearAllJobs() { this.jobs = []; this.job_map = {}; this.update(); } function send(path, is_remote) { var to; var show_hidden; if (is_remote) { to = file_transfer.local_folder_view.fd.path; show_hidden = file_transfer.remote_folder_view.show_hidden; } else { to = file_transfer.remote_folder_view.fd.path; show_hidden = file_transfer.local_folder_view.show_hidden; } if (!to) return; to += handler.get_path_sep(!is_remote) + getFileName(is_remote, path); var id = jobIdCounter; jobIdCounter += 1; this.jobs.push({ type: "transfer", id: id, path: path, to: to, include_hidden: show_hidden, is_remote: is_remote, is_last: false }); this.job_map[id] = this.jobs[this.jobs.length - 1]; handler.send_files(id, path, to, 0, show_hidden, is_remote); var self = this; self.timer(30ms, function() { self.update(); }); } function addJob(id, path, to, file_num, show_hidden, is_remote) { var job = { type: "transfer", id: id, path: path, to: to, include_hidden: show_hidden, is_remote: is_remote, is_last: true, file_num: file_num }; this.jobs.push(job); this.job_map[id] = this.jobs[this.jobs.length - 1]; jobIdCounter = id + 1; handler.add_job(id, path, to, file_num, show_hidden, is_remote); stdout.println(JSON.stringify(job)); } function continueJob(id) { var job = this.job_map[id]; if (job == null || !job.is_last){ return; } job.is_last = false; handler.resume_job(job.id, job.is_remote); } function addDelDir(path, is_remote) { var id = jobIdCounter; jobIdCounter += 1; this.jobs.push({ type: "del-dir", id: id, path: path, is_remote: is_remote }); this.job_map[id] = this.jobs[this.jobs.length - 1]; this.update(); } function addDelFile(path, is_remote) { var id = jobIdCounter; jobIdCounter += 1; this.jobs.push({ type: "del-file", id: id, path: path, is_remote: is_remote }); this.job_map[id] = this.jobs[this.jobs.length - 1]; this.update(); } function confirmDeletePolling(is_remote) { for(var i=0;i n) i = n; var res = i + ' / ' + n + " " + translate("files"); if (job.total_size > 0) { var s = getSize(0, job.finished_size); if (s) s += " / "; res += ", " + s + getSize(0, job.total_size); } // below has problem if some file skipped var percent = job.total_size == 0 ? 100 : (100. * job.finished_size / job.total_size).toInteger(); // (100. * i / (n || 1)).toInteger(); if (job.finished) percent = '100'; if (percent) res += ", " + percent + "%"; if (job.finished) res = translate("Finished") + " " + res; if (job.speed) res += ", " + getSize(0, job.speed) + "/s"; return res; } function updateJob(job) { var el = this.select("div[id=s" + job.id + "]"); if (el) el.text = this.getStatus(job); } function updateJobStatus(id, file_num = -1, err = null, speed = null, finished_size = 0) { var job = this.job_map[id]; if (job.type == "del-file"){ job.finished = true; job.err = err; refreshDir(job.is_remote); this.updateJob(job); return; } if (!job) return; if (file_num < job.file_num) return; job.file_num = file_num; var n = job.num_entries || job.entries.length; job.finished = job.file_num >= n - 1 || err == "cancel"; job.finished_size = finished_size; job.speed = speed || 0; this.updateJob(job); if (job.type == "del-dir") { if (job.finished) { if (!err) { handler.remove_dir(job.id, job.path, job.is_remote); refreshDir(job.is_remote); if (is_remote) file_transfer.remote_folder_view.table.resetCurrent(); else file_transfer.local_folder_view.table.resetCurrent(); } } else if (!job.no_confirm) { handler.confirm_delete_files(id, job.file_num + 1); } } else if (job.finished || file_num == -1) { refreshDir(!job.is_remote); } } function renderRow(job, i) { var svg = this.getSvg(job); return {svg}
{job.path}
{this.getStatus(job)}
{svg_continue}
{svg_cancel} ; } } class FolderView : Reactor.Component { this var fd = {}; this var history = []; this var show_hidden = false; function sep() { return handler.get_path_sep(this.is_remote); } function this(params) { this.is_remote = params.is_remote; if (this.is_remote) { this.show_hidden = !!handler.get_option("remote_show_hidden"); } else { this.show_hidden = !!handler.get_option("local_show_hidden"); } if (!this.is_remote) { var dir = handler.get_option("local_dir"); if (dir) { this.fd = handler.read_dir(dir, this.show_hidden); if (this.fd) return; } this.fd = handler.read_dir(handler.get_home_dir(), this.show_hidden); } } // sort predicate function foldersFirst(a, b) { if (a.type <= 3 && b.type > 3) return -1; if (a.type > 3 && b.type <= 3) return +1; if (a.name == b.name) return 0; return a.name.toLowerCase().lexicalCompare(b.name.toLowerCase()); } function render() { return
{this.renderTitle()} {this.renderNavBar()} {this.renderOpBar()} {this.renderTable()}
; } function renderTitle() { return
{svg_computer}
{platformSvg(handler.get_platform(this.is_remote), "white")}
{translate(this.is_remote ? "Remote Computer" : "Local Computer")}
} function renderNavBar() { return
{svg_home}
{svg_arrow}
{svg_arrow}
{this.renderSelect()}
{svg_refresh}
; } function renderSelect() { return ; } function renderOpBar() { if (this.is_remote) { return
{svg_send}{translate('Receive')}
{svg_add_folder}
{svg_trash}
; } return
{svg_add_folder}
{svg_trash}
{translate('Send')}{svg_send}
; } function get_updated() { this.table.sortRows(false); if (this.fd && this.fd.path) this.select_dir.value = this.fd.path; } function renderTable() { var fd = this.fd; var entries = fd.entries || []; var table = this.table; if (!table || !table.sortBy) { entries.sort(this.foldersFirst); } var me = this; var path = fd.path; if (path != "/" && path) { entries = [{ name: "..", type: 1 }].concat(entries); } var rows = entries.map(function(e) { return me.renderRow(e); }); var id = (this.is_remote ? "remote" : "local") + "-folder-view"; return {rows}
  • {svg_checkmark}{translate('Show Hidden Files')}
  • {translate('Name')}{translate('Modified')}{translate('Size')}
    ; } function joinPath(name) { var path = this.fd.path; if (path == "/") { if (this.sep() == "/") return this.sep() + name; else return name; } return path + (path[path.length - 1] == this.sep() ? "" : this.sep()) + name; } function attached() { var me = this; this.table.onRowDoubleClick = function (row) { var type = row[0].attributes["type"]; if (type > 3) return; var name = row[1].text; var path = name == ".." ? getParentPath(me.is_remote, me.fd.path) : me.joinPath(name); me.table.resetCurrent(); me.goto(path, true); } this.get_updated(); } function goto(path, push) { if (!path) return; if (this.sep() == "\\" && path.length == 2) { // windows drive path += "\\"; } if (push) this.pushHistory(); if (this.is_remote) { handler.read_remote_dir(path, this.show_hidden); } else { var fd = handler.read_dir(path, this.show_hidden); this.refresh({ fd: fd }); } } function refresh(data) { if (!data.fd || !data.fd.path) return; if (this.is_remote && !remote_home_dir) { remote_home_dir = data.fd.path; } this.update(data); var me = this; self.timer(1ms, function() { me.get_updated(); }); } function renderRow(entry) { var path; if (this.is_remote) { path = handler.get_icon_path(entry.type, getExt(entry.name)); } else { path = this.joinPath(entry.name); } var tm = entry.time ? new Date(entry.time.toFloat() * 1000.).toLocaleString() : 0; return {entry.name} {tm || ""} {getSize(entry.type, entry.size)} ; } event click $(#switch-hidden) { this.show_hidden = !this.show_hidden; this.refreshDir(); } event click $(.goup) () { var path = this.fd.path; if (!path || path == "/") return; path = getParentPath(this.is_remote, path); this.goto(path, true); } event click $(.goback) () { var path = this.history.pop(); if (!path) return; this.goto(path, false); } event click $(.trash) () { var rows = this.getCurrentRows(); if (!rows || rows.length == 0) return; var delete_dirs = new Array(); for (var i = 0; i < rows.length; ++i) { var row = rows[i]; var path = row[0]; var type = row[1]; var new_history = []; for (var j = 0; j < this.history.length; ++j) { var h = this.history[j]; if ((h + this.sep()).indexOf(path + this.sep()) == -1) new_history.push(h); } this.history = new_history; if (type == 1) { file_transfer.job_table.addDelDir(path, this.is_remote); } else { file_transfer.job_table.addDelFile(path, this.is_remote); } } file_transfer.job_table.confirmDeletePolling(this.is_remote); } event click $(.add-folder) () { var me = this; msgbox("custom", translate("Create Folder"), "
    \
    " + translate("Please enter the folder name") + ":
    \
    \
    ", function(res=null) { if (!res) return; if (!res.name) return; var name = res.name.trim(); if (!name) return; if (name.indexOf(me.sep()) >= 0) { handler.msgbox("custom-error", "Create Folder", "Invalid folder name"); return; } var path = me.joinPath(name); handler.create_dir(jobIdCounter, path, me.is_remote); create_dir_jobs[jobIdCounter] = { is_remote: me.is_remote, path: path }; jobIdCounter += 1; }); } function refreshDir() { this.goto(this.fd.path, false); } event click $(.refresh) () { this.refreshDir(); } event click $(.home) () { var path = this.is_remote ? remote_home_dir : handler.get_home_dir(); if (!path) return; if (path == this.fd.path) return; this.goto(path, true); } function getCurrentRow() { var row = this.table.getCurrentRow(); if (!row) return; var name = row[1].text; if (!name || name == "..") return; var type = row[0].attributes["type"]; return [this.joinPath(name), type]; } function getCurrentRows() { var rows = this.table.getCurrentRows(); if (!rows || rows.length== 0) return; var records = new Array(); for (var i = 0; i < rows.length; ++i) { var name = rows[i][1].text; if (!name || name == "..") continue; var type = rows[i][0].attributes["type"]; records.push([this.joinPath(name), type]); } return records; } event click $(.send) () { var rows = this.getCurrentRows(); if (!rows || rows.length == 0) return; for (var i = 0; i < rows.length; ++i) { file_transfer.job_table.send(rows[i][0], this.is_remote); } } event change $(.select-dir) (_, el) { var x = getTime() - last_key_time; if (x < 1000) return; if (this.fd.path != el.value) { this.goto(el.value, true); } } event keydown $(.select-dir) (evt, me) { if (isEnterKey(evt)) { this.goto(me.value, true); } } function pushHistory() { var path = this.fd.path; if (!path) return; if (path != this.history[this.history.length - 1]) this.history.push(path); } } var file_transfer; class FileTransfer: Reactor.Component { function this() { file_transfer = this; } function render() { return
    ; } } function initializeFileTransfer() { $(#file-transfer-wrapper).content(); $(#video-wrapper).style.set { visibility: "hidden", position: "absolute" }; $(#file-transfer-wrapper).style.set { display: "block" }; } handler.updateFolderFiles = function(fd) { // stdout.println("update folder files: " + JSON.stringify(fd)); fd.entries = fd.entries || []; if (fd.id > 0) { var jt = file_transfer.job_table; var job = jt.job_map[fd.id]; if (job) { job.file_num = -1; job.total_size = fd.total_size; job.entries = fd.entries; job.num_entries = fd.num_entries; file_transfer.job_table.updateJobStatus(job.id); } } else { file_transfer.remote_folder_view.refresh({ fd: fd }); } } handler.jobProgress = function(id, file_num, speed, finished_size) { file_transfer.job_table.updateJobStatus(id, file_num, null, speed, finished_size); } handler.jobDone = function(id, file_num = -1) { var job = create_dir_jobs[id]; if (job) { refreshDir(job.is_remote); return; } file_transfer.job_table.updateJobStatus(id, file_num); } handler.jobError = function(id, err, file_num = -1) { var job = deleting_single_file_jobs[id]; if (job) { msgbox("custom-error", "Delete File", err); return; } job = create_dir_jobs[id]; if (job) { msgbox("custom-error", "Create Folder", err); return; } if (file_num < 0) { handler.msgbox("custom-error", "Failed", err); } file_transfer.job_table.updateJobStatus(id, file_num, err); } handler.clearAllJobs = function() { file_transfer.job_table.clearAllJobs(); } handler.addJob = function (id, path, to, file_num, show_hidden, is_remote) { // stdout.println("restore job: " + is_remote); file_transfer.job_table.addJob(id,path,to,file_num,show_hidden,is_remote); } handler.updateTransferList = function () { file_transfer.job_table.update(); } function refreshDir(is_remote) { if (is_remote) file_transfer.remote_folder_view.refreshDir(); else file_transfer.local_folder_view.refreshDir(); } var deleting_single_file_jobs = {}; var create_dir_jobs = {} function confirmDelete(id ,path, is_remote) { msgbox("custom-skip", "Confirm Delete", "
    \
    " + translate('Are you sure you want to delete this file?') + "
    \ " + path + "
    \ ", function(res=null) { if (!res) { file_transfer.job_table.updateJobStatus(id, -1, "cancel"); file_transfer.job_table.cancelDeletePolling(); } else if (res.skip) { file_transfer.job_table.updateJobStatus(id, -1, "cancel"); file_transfer.job_table.confirmDeletePolling(is_remote); } else { handler.remove_file(id, path, 0, is_remote); if (is_remote) file_transfer.remote_folder_view.table.resetCurrent(); else file_transfer.local_folder_view.table.resetCurrent(); deleting_single_file_jobs[id] = { is_remote: is_remote, path: path }; file_transfer.job_table.confirmDeletePolling(is_remote); } }); } handler.confirmDeleteFiles = function(id, i, name) { var jt = file_transfer.job_table; var job = jt.job_map[id]; if (!job) return; var n = job.num_entries; if (i >= n) return; var file_path = job.path; if (name) file_path += handler.get_path_sep(job.is_remote) + name; msgbox("custom-skip", "Confirm Delete", "
    \
    " + translate('Deleting') + " #" + (i + 1) + " / " + n + " " + translate('files') + ".
    \
    " + translate('Are you sure you want to delete this file?') + "
    \ " + file_path + "
    \
    " + translate('Do this for all conflicts') + "
    \ ", function(res=null) { if (!res) { jt.updateJobStatus(id, i - 1, "cancel"); file_transfer.job_table.cancelDeletePolling(); } else if (res.skip) { if (res.remember){ jt.updateJobStatus(id, i, "cancel"); } else{ handler.jobDone(id, i); } file_transfer.job_table.confirmDeletePolling(job.is_remote); } else { job.no_confirm = res.remember; if (job.no_confirm){ handler.set_no_confirm(id); file_transfer.job_table.confirmDeletePolling(job.is_remote); } handler.remove_file(id, file_path, i, job.is_remote); } if(i+1 >= n){ file_transfer.job_table.confirmDeletePolling(job.is_remote); } }); } handler.overrideFileConfirm = function(id, file_num, to, is_upload) { var jt = file_transfer.job_table; msgbox("custom-skip", "Confirm Write Strategy", "
    \
    " + translate('Overwrite') + translate('files') + ".
    \
    " + translate('This file exists, skip or overwrite this file?') + "
    \ " + to + "
    \
    " + translate('Do this for all conflicts') + "
    \ ", function(res=null) { if (!res) { jt.updateJobStatus(id, -1, "cancel"); handler.cancel_job(id); } else if (res.skip) { if (res.remember){ handler.set_write_override(id,file_num,false,true, is_upload); // } else { handler.set_write_override(id,file_num,false,false,is_upload); // } } else { if (res.remember){ handler.set_write_override(id,file_num,true,true,is_upload); // } else { handler.set_write_override(id,file_num,true,false,is_upload); // } } }); } function save_file_transfer_close_state() { var local_dir = file_transfer.local_folder_view.fd.path || ""; var local_show_hidden = file_transfer.local_folder_view.show_hidden ? "Y" : ""; var remote_dir = file_transfer.remote_folder_view.fd.path || ""; var remote_show_hidden = file_transfer.remote_folder_view.show_hidden ? "Y" : ""; handler.save_close_state("local_dir", local_dir); handler.save_close_state("local_show_hidden", local_show_hidden); handler.save_close_state("remote_dir", remote_dir); handler.save_close_state("remote_show_hidden", remote_show_hidden); }