Commit a2faab4b authored by Mihkel Putrinš's avatar Mihkel Putrinš
Browse files
parents
<!DOCTYPE html>
<html>
<head>
<title>Keeleressurside Panustaja</title>
<meta charset="UTF-8" />
<link rel="stylesheet" href="../node_modules/font-awesome/css/font-awesome.min.css">
<link href="style.css" rel="stylesheet" />
<script type="text/javascript" src="../node_modules/jquery/dist/jquery.js"></script>
<script src="./script.js" charset="utf-8"></script>
</head>
<body>
<input id="chdir_input" type="file" nwdirectory style="display:none" />
<div>
<table id="cards">
<tr>
<td id="hello_card_label" translate="hello_card" class="card_title current">Hello</td>
<td id="select_card_label" translate="select_card" class="card_title">Select</td>
<td id="upload_card_label" translate="upload_card" class="card_title">Upload</td>
<td id="thankyou_card_label" translate="thankyou_card" class="card_title">Thank You</td>
</tr>
<tr>
<td colspan="4">
<div id="card_contents" style="overflow: auto; height:800px">
<card id="hello_card">
<div translate="hello_text" class="infotext"></div>
<button id="login_button" translate="login_button">.</button>
</card>
<card id="select_card">
<div translate="select_text" class="infotext"></div>
<button id="chdir_button">.</button>
</card>
<card id="upload_card">
<div translate="upload_text" class="infotext"></div>
<div id="upload_stats"></div>
<label translate="resource_name">resource_name</label>
<input id="resource_name" />
<div id="overquota_total" translate="overquota_total" class="warning">
</div>
<div id="overquota_files" class="warning">
</div>
<button id="back_button" style="display:inline;" translate="back_button">.</button>
<button id="upload_button" style="display:inline;" translate="upload_button">.</button>
<div id="local_fs_tree"></div>
</card>
<card id="thankyou_card">
<div translate="thankyou_text" class="infotext">
</div>
</card>
<div id="uploaded_resource_list" style="display:none" translate="uploaded_resource_list"></div>
</div>
</td>
</tr>
</table>
<div id="waitspinner"></div>
<div id="upload_progress">
<div id="upload_back" progress></div>
<div id="upload_front" progress></div>
</div>
</div>
</body>
</html>
\ No newline at end of file
// 1. core modules
var util = require('util')
var fs = require('fs')
var path = require('path')
var gui = global.window.nwDispatcher.requireNwGui()
// 2. public modules from npm
var moment = require('moment')
// 3. Own modules
var bytesToSize = require('./bytesToSize.js')
var msToTime = require('./msToTime.js')
var progress = require('./progress.js')
var configuration = require('./configuration.json')
var translate = require('./translations/estonian.js')
var loadDirs = function loadDirs() {
var my_nodes = {}
var root_node = {}
var rec_counter = 1
var files_total = 0
var dirs_total = 0
var bytes_total = 0
var overquota_total = 0
var overquota_files_total = 0
var decreaseCounter = function decreaseCounter(finalCallback) {
if (rec_counter === 1) {
finalCallback()
} else {
rec_counter --
}
}
// Decrease this, if statusline doesnot refresh often enough
var load_factor = 4.1
var ticker = Math.round((new Date()).getTime() / 1000)
var nodes_per_sec = 0
var loadRec = function(parent_node, full_path, finalCallback) {
if (ticker === Math.round((new Date()).getTime() / 1000)) {
nodes_per_sec ++
} else {
// console.log(ticker + ':' + nodes_per_sec + ' nodes. Active threads: ' + rec_counter)
$('[progress]').text(nodes_per_sec + '/sec;' + displayNodePath(path.dirname(full_path), path.basename(full_path)))
ticker = Math.round((new Date()).getTime() / 1000)
nodes_per_sec = 0
}
var wait_ms = Math.round(rec_counter/load_factor)
setTimeout(function() {
fs.stat(full_path, function(err, stats) {
if(stats === undefined) {
decreaseCounter(finalCallback)
return
}
// console.log('Loading', full_path, finalCallback)
if (my_nodes[stats.ino] !== undefined) {
decreaseCounter(finalCallback)
return
}
var new_node = my_nodes[stats.ino] = {
ino: stats.ino,
full_path: full_path,
dirname: path.dirname(full_path),
basename: path.basename(full_path),
entuname: path.basename(full_path),
size: stats.isDirectory() ? 0 : stats.size,
overquota: (stats.size > configuration.__MAX_FILESIZE_2G ? true : false),
parent: parent_node,
is_dir: stats.isDirectory(),
file_count: 0,
dir_count: 0,
nodes: {}
}
if (parent_node === null) {
root_node = new_node
}
if (parent_node !== null) {
parent_node.nodes[stats.ino] = new_node
}
// console.log('New node ', Object.keys(my_nodes).length, JSON.stringify({count:rec_counter, path:full_path}))
if (stats.isDirectory()) {
if (parent_node !== null) {
parent_node.dir_count ++
}
dirs_total ++
fs.readdir(full_path, function(err, files) {
if (files !== undefined) {
files.forEach(function(childname) {
if (configuration.__EXCLUDE_FILENAMES.indexOf(childname) === -1) {
rec_counter ++
loadRec(my_nodes[stats.ino], path.join(full_path, childname), finalCallback)
}
})
}
decreaseCounter(finalCallback)
})
} else if (stats.isFile()) {
if (parent_node !== null) {
parent_node.file_count ++
}
files_total ++
bytes_total += stats.size
decreaseCounter(finalCallback)
} else {
decreaseCounter(finalCallback)
}
})
}, wait_ms)
}
var displayNodePath = function displayNodePath(dirname_in, basename_in) {
var dirname = dirname_in
var basename = basename_in
var max_length = 40
var node_path = path.resolve(dirname, basename)
var diff = node_path.length - max_length
var half_len = Math.round(max_length / 2)
// console.log(node_path, node_path.length, diff)
if (diff > 0) {
diff += 1
var basename_intact = false
var basename_diff = basename.length - half_len
if (basename_diff > 0) {
var crop_size = Math.min(diff, basename_diff)
basename = basename.slice(crop_size)
diff = diff - crop_size
} else {
basename_intact = true
}
var dirname_diff = dirname.length - half_len + 1
if (dirname_diff > 0) {
var crop_size = Math.min(diff, dirname_diff)
dirname = dirname.slice(0, dirname.length - crop_size)
}
var ret_value = dirname + '' + basename
if (basename_intact)
ret_value = path.resolve(dirname + '', basename)
if (ret_value === undefined)
// console.log(dirname, basename, dirname_in, basename_in)
$('[progress]').text(ret_value)
return ret_value
}
}
return {
bytes_total: function () {
return bytes_total
},
root_node: function () {
return root_node
},
nodes: function () {
return my_nodes
},
load: function (full_path, finalCallback) {
// console.log(my_nodes, 'Loading', full_path)
loadRec(null, full_path, finalCallback)
},
reset: function () {
my_nodes = {}
rec_counter = 1
output_counter = 0
files_total = 0
dirs_total = 0
bytes_total = 0
overquota_total = 0
overquota_files_total = 0
// console.log(my_nodes, 'Resetting')
$('#upload_stats').empty()
$('#overquota_total').empty()
$('#overquota_files').empty()
$('[progress]').text('...')
},
displayNodes: function (ms_so_far) {
var now_ms = (new Date()).getTime()
if (files_total + dirs_total !== Object.keys(my_nodes).length) {
// console.log('Warning: ' + files_total + ' + ' + dirs_total + ' !== ' + Object.keys(my_nodes).length + '!')
}
$('#upload_stats')
.append($('<div>')
.append('<label>' + translate('dirs_total') + '</label>')
.append('<span id="dirs_total">' + dirs_total + '</span>')
)
.append($('<div>')
.append('<label>' + translate('files_total') + '</label>')
.append('<span id="files_total">' + files_total + '</span>')
)
.append($('<div>')
.append('<label>' + translate('bytes_total') + '</label>')
.append('<span id=bytes_total>' + bytesToSize(bytes_total) + '</span>')
)
var node_keys = Object.keys(my_nodes)
for (var i = 0; i < node_keys.length; i++) {
var ino = node_keys[i]
var current_node = my_nodes[ino]
// console.log(i, ino)
if (current_node.overquota) {
// console.log(current_node.size + ' > ' + configuration.__MAX_FILESIZE_2G)
bytes_total = bytes_total - current_node.size
overquota_total = overquota_total + current_node.size
files_total = files_total - 1
overquota_files_total = overquota_files_total + 1
var cn_path = displayNodePath(current_node.dirname, current_node.basename)
$('#overquota_files').append(
$('<li class="hoverlink"><a>' + cn_path + '<span class="right">' + bytesToSize(current_node.size) + '</span></a></li>')
.prop('path', path.resolve(current_node.dirname, current_node.basename))
.click(function(event) {
gui.Shell.showItemInFolder($(this).prop('path'))
})
.mouseenter(function(event) {
$('[progress]').text($(this).prop('path'))
})
.mouseleave(function(event) {
$('[progress]').text(
(dirs_total + files_total + overquota_files_total)
+ ' faili ja kataloogi lugemiseks kulus '
+ msToTime($('#upload_stats').prop('load_time_total')) + '.' )
})
)
}
}
$('#bytes_total').text(bytesToSize(bytes_total))
$('#files_total').text(files_total)
if ($('#overquota_files').children().size() > 0) {
$('#overquota_files').show()
$('#overquota_files').before('<h3>' + overquota_files_total + ' fail' + (overquota_files_total ? 'i' : '') + ' (' + bytesToSize(overquota_total) + ') jääb praegu upitamata</h3>')
$('#overquota_files').before('<p>' + (overquota_files_total ? 'Nende' : 'Selle') + ' repositooriumisse lisamiseks helista või kirjuta meile.</p>')
} else {
$('#overquota_files').hide()
}
if (bytes_total > configuration.__UPLOAD_QUOTA_20G) {
$('#upload_button').hide()
$('#overquota_total').show()
} else {
$('#overquota_total').hide()
$('#upload_button').show()
}
$('#upload_stats')
.prop('load_time_total', (new Date()).getTime() - now_ms + ms_so_far)
// .append($('<div>')
// .append('<label>' + translate('load_time_total') + '</label>')
// .append('<span>' + msToTime((new Date()).getTime() - now_ms + ms_so_far) + '</span>')
// )
$('[progress]').text(
(dirs_total + files_total + overquota_files_total)
+ ' faili ja kataloogi lugemiseks kulus '
+ msToTime($('#upload_stats').prop('load_time_total')) + '.' )
}
}
}
module.exports = loadDirs()
var msToDate = function msToDate(ms, without_time) {
if (isNaN(ms)) ms = 0
try {
d = new Date(ms)
if (without_time) {
return d.toISOString().replace(/T/, ' ').replace(/:/g, '-').replace(/\..+/, '').split(' ')[0]
} else {
return d.toDateString() + ' ' + d.toTimeString()
}
} catch (exception) {
console.log('Cant convert "' + ms + '" ms.', d)
throw exception
}
}
module.exports = msToDate
var msToTime = function msToTime(interval) {
if (isNaN(interval)) interval = 0
if (interval < 1000) return '0 sekundit'
var ms = interval % 1000
interval = (interval - ms) / 1000
var secs = interval % 60
interval = (interval - secs) / 60
var mins = interval % 60
interval = (interval - mins) / 60
var hrs = interval % 24
interval = (interval - hrs) / 24
var days = interval % 7
var weeks = (interval - days) / 7
var human_interval = []
if (weeks > 0) {
human_interval.push(weeks + ' nädal' + (weeks > 1 ? 'at' : ''))
}
if (days > 0) {
human_interval.push(days + ' päev' + (days > 1 ? 'a' : ''))
}
if (hrs > 0) {
human_interval.push(hrs + ' tund' + (hrs > 1 ? 'i' : ''))
}
if (mins > 0) {
human_interval.push(mins + ' minut' + (mins > 1 ? 'it' : ''))
}
if (secs > 0) {
human_interval.push(secs + ' sekund' + (secs > 1 ? 'it' : ''))
}
return human_interval.join(' ')
}
module.exports = msToTime
var msToTime = require('./msToTime.js')
var msToDate = require('./msToDate.js')
var metrics
var CB = {}
var progress = function progress() {
var initMetrics = function initMetrics() {
metrics = {
total: 0,
current: 0,
left: 0,
start_time_ms: undefined, // timestamp
advance_time_ms: undefined, // timestamp of last advance
elapsed_ms: 0, // advance_time_ms - start_time_ms
eta_ms: 0, // ms, estimated time left
eta_time: '', // humanize(eta_ms)
eta_date: '',
counter: 0,
percentage: 0,
last_path: 'N/A',
registered: false,
started: false,
loading: false,
uploading: false,
last_refresh_ms: 0, // timestamp of last refresh
last_amount: 0, // amount of data between last two refreshes
last_interval_ms: 0, // interval between last two refreshes
mov_ave_pool_ms: 100000, // length of moving average calculation pool
mov_ave_pool_bytes: 0, // moving average of bytes transferred in last mov_ave_pool_ms
mov_ave_speed: 0, // bytes/ms, mov_ave_pool_bytes / min(elapsed_ms, mov_ave_pool_ms)
done: false
}
}
initMetrics()
var refresh_interval_target = 1000
var refresh_interval = refresh_interval_target
var refresh = function refresh() {
if (!metrics.started)
return
// console.log(metrics)
var d = new Date()
var now_ms = d.getTime()
if (metrics.last_refresh_ms === 0)
metrics.last_refresh_ms = now_ms
metrics.last_interval_ms = now_ms - metrics.last_refresh_ms
if (metrics.last_interval_ms > refresh_interval_target) {
refresh_interval--
} else if (metrics.last_interval_ms < refresh_interval_target) {
refresh_interval++
}
if (metrics.elapsed_ms > metrics.mov_ave_pool_ms) {
metrics.mov_ave_pool_bytes -= Math.round(metrics.mov_ave_pool_bytes * metrics.last_interval_ms / metrics.mov_ave_pool_ms)
}
metrics.mov_ave_pool_bytes += metrics.last_amount
metrics.mov_ave_speed = Math.round( metrics.mov_ave_pool_bytes / Math.min(metrics.elapsed_ms, metrics.mov_ave_pool_ms) )
metrics.eta_ms = metrics.left / metrics.mov_ave_speed
metrics.eta_time = msToTime(metrics.eta_ms)
metrics.eta_date = msToDate(metrics.advance_time_ms + metrics.eta_ms)
metrics.percentage = metrics.current / metrics.total * 100
if (metrics.left <= 0) {
metrics.done = true
}
if (metrics.left < 0) {
// console.log('Metrics inconsistent', metrics)
}
metrics.last_amount = 0
metrics.last_refresh_ms = now_ms
}
return {
metrics: metrics,
register_callbacks: function(started_cb, increased_cb, decreased_cb, advanced_cb, output_cb, done_cb) {
CB['started_cb'] = started_cb
CB['increased_cb'] = increased_cb
CB['decreased_cb'] = decreased_cb
CB['advanced_cb'] = advanced_cb
CB['output_cb'] = output_cb
CB['done_cb'] = done_cb
metrics.registered = true
CB.output_cb(metrics)
return metrics
},
start: function() {
if (!metrics.registered) throw "Callbacks not registered!"
if (metrics.started) throw "Allready started!"
var d = new Date()
metrics.start_time_ms = metrics.advance_time_ms = d.getTime()
metrics.started = true
CB.started_cb(metrics)
var output_rec = function output_rec () {
if (!metrics.done) {
refresh()
CB.output_cb(metrics)
// console.log(metrics + ' | Next output in ' + refresh_interval + ' ms.')
setTimeout(function() {output_rec()}, refresh_interval)
}
}
// var output_rec = function output_rec () {
// if (!metrics.done) {
// if (metrics.started) {
// CB.output_cb(metrics)
// } else {
// metrics.started = true
// CB.started_cb(metrics)
// }
// }
// }
output_rec()
},
restart: function() {
if (!metrics.registered) throw "Callbacks not registered!"
initMetrics()
var d = new Date()
metrics.start_time_ms = metrics.advance_time_ms = d.getTime()
metrics.started = true
CB.started_cb(metrics)
},
increase: function(amount) {
if (!metrics.registered) throw "Callbacks not registered!"
metrics.loading = true
// console.log(metrics, amount)
amount = Number(amount)
metrics.total += amount
// console.log(metrics, amount)
metrics.left += amount
CB.increased_cb(metrics)
},
decrease: function(amount) {
if (!metrics.registered) throw "Callbacks not registered!"
amount = Number(amount)
metrics.total -= amount
metrics.left -= amount
// if (isNaN(metrics.left))
// console.log(metrics.last_amount, metrics)
CB.decreased_cb(metrics)
},
advance: function(amount) {
if (!metrics.registered) throw "Callbacks not registered!"
metrics.loading = false
metrics.uploading = true
var d = new Date()
if (!metrics.started) {
this.start()
}
if (amount === undefined) {
amount = 0
}
metrics.current += amount
metrics.left -= amount
metrics.advance_time_ms = d.getTime()
metrics.elapsed_ms = metrics.advance_time_ms - metrics.start_time_ms