Merge branch 'master' into master

This commit is contained in:
djazz
2024-07-21 17:20:49 +02:00
committed by GitHub
8 changed files with 1438 additions and 310 deletions

View File

@@ -1,4 +1,4 @@
FROM node:20-alpine
FROM node:22-alpine
# Create app directory
WORKDIR /usr/src/app
@@ -16,6 +16,12 @@ RUN wget https://web.archive.org/web/20150803131026if_/https://kindlegen.s3.amaz
chmod +x /usr/local/bin/kindlegen && \
rm -rf kindlegen
RUN apk add --no-cache pipx
ENV PATH="$PATH:/root/.local/bin"
RUN pipx install pdfCropMargins
# Copy files needed by npm install
COPY package*.json ./

240
index.js
View File

@@ -1,18 +1,20 @@
#!/usr/bin/env node
const http = require('http')
const Koa = require('koa')
const Router = require('@koa/router')
const multer = require('@koa/multer')
const logger = require('koa-logger')
const sendfile = require('koa-sendfile')
const serve = require('koa-static')
const mount = require('koa-mount')
const { mkdirp } = require('mkdirp')
const fs = require('fs')
const { spawn } = require('child_process')
const { join, extname, basename, dirname } = require('path')
const resolvepath = require('path').resolve
const FileType = require('file-type')
const { transliterate } = require('transliteration')
const sanitize = require('sanitize-filename')
const port = 3001
const expireDelay = 30 // 30 seconds
@@ -78,7 +80,12 @@ function expireKey (key) {
function flash (ctx, data) {
console.log(data)
ctx.cookies.set('flash', encodeURIComponent(JSON.stringify(data)), {overwrite: true, httpOnly: false, sameSite: 'strict', maxAge: 10 * 1000})
//ctx.cookies.set('flash', encodeURIComponent(JSON.stringify(data)), {overwrite: true, httpOnly: false, sameSite: 'strict', maxAge: 10 * 1000})
ctx.response.status = data.success ? 200 : 400
if (!data.success) {
ctx.set("Connection", "close")
}
ctx.body = data.message
}
const app = new Koa()
@@ -104,18 +111,18 @@ const upload = multer({
fileFilter: (req, file, cb) => {
// Fixes charset
// https://github.com/expressjs/multer/issues/1104#issuecomment-1152987772
file.originalname = doTransliterate(Buffer.from(file.originalname, 'latin1').toString('utf8'))
file.originalname = sanitize(Buffer.from(file.originalname, 'latin1').toString('utf8'))
console.log('Incoming file:', file)
const key = req.body.key.toUpperCase()
if (!app.context.keys.has(key)) {
console.error('FileFilter: Unknown key: ' + key)
cb(null, false)
cb("Unknown key " + key, false)
return
}
if ((!allowedTypes.includes(file.mimetype) && file.mimetype != "application/octet-stream") || !allowedExtensions.includes(extname(file.originalname.toLowerCase()).substring(1))) {
console.error('FileFilter: File is of an invalid type ', file)
cb(null, false)
cb("Invalid filetype: " + JSON.stringify(file), false)
return
}
cb(null, true)
@@ -154,13 +161,39 @@ router.post('/generate', async ctx => {
if(ctx.keys.get(key) === info) removeKey(key)
}, maxExpireDuration * 1000)
ctx.cookies.set('key', key, {overwrite: true, httpOnly: false, sameSite: 'strict', maxAge: expireDelay * 1000})
ctx.body = key
})
router.get('/download/:key', async ctx => {
const key = ctx.params.key.toUpperCase()
const key = ctx.cookies.get('key')
if (!key) {
await next()
return
}
const info = ctx.keys.get(key)
if (!info || !info.file) {
await next()
return
}
ctx.redirect('/' + encodeURIComponent(info.file.name));
})
async function downloadFile (ctx, next) {
const key = ctx.cookies.get('key')
if (!key) {
await next()
return
}
const filename = decodeURIComponent(ctx.params.filename)
const info = ctx.keys.get(key)
if (!info || !info.file || info.file.name !== filename) {
await next()
return
}
if (info.agent !== ctx.get('user-agent')) {
@@ -168,20 +201,31 @@ router.get('/download/:key', async ctx => {
return
}
expireKey(key)
// const fallback = basename(info.file.path)
const sanename = info.file.name.replace(/[^\.\w\-"'\(\)]/g, '_')
console.log('Sending file', [info.file.path, info.file.name, sanename])
await sendfile(ctx, info.file.path)
console.log('Sending file', [info.file.path, info.file.name])
if (info.agent.includes('Kindle')) {
// Kindle needs a safe name or it thinks it's an invalid file
ctx.attachment(sanename)
} else {
// Kobo always uses fallback
ctx.attachment(info.file.name, {fallback: sanename})
ctx.attachment(info.file.name)
}
})
await sendfile(ctx, info.file.path)
}
router.post('/upload', async (ctx, next) => {
try {
await upload.single('file')(ctx, () => {})
} catch (err) {
flash(ctx, {
message: err,
success: false
})
// ctx.throw(400, err)
// ctx.res.end(err)
await next()
return
}
ctx.res.writeContinue()
router.post('/upload', upload.single('file'), async ctx => {
const key = ctx.request.body.key.toUpperCase()
if (ctx.request.file) {
@@ -193,13 +237,13 @@ router.post('/upload', upload.single('file'), async ctx => {
message: 'Unknown key ' + key,
success: false
})
ctx.redirect('back', '/')
if (ctx.request.file) {
fs.unlink(ctx.request.file.path, (err) => {
if (err) console.error(err)
else console.log('Removed file', ctx.request.file.path)
})
}
await next()
return
}
@@ -219,16 +263,17 @@ router.post('/upload', upload.single('file'), async ctx => {
if (ctx.request.file) {
if (ctx.request.file.size === 0) {
flash(ctx, {
message: 'Invalid file submitted',
let data = {
message: 'Invalid file submitted (empty file)',
success: false,
key: key
})
ctx.redirect('back', '/')
}
flash(ctx, data)
fs.unlink(ctx.request.file.path, (err) => {
if (err) console.error(err)
else console.log('Removed file', ctx.request.file.path)
})
await next()
return
}
@@ -250,28 +295,46 @@ router.post('/upload', upload.single('file'), async ctx => {
success: false,
key: key
})
ctx.redirect('back', '/')
fs.unlink(ctx.request.file.path, (err) => {
if (err) console.error(err)
else console.log('Removed file', ctx.request.file.path)
})
await next()
return
}
let data = null
filename = ctx.request.file.originalname
if (ctx.request.body.transliteration) {
filename = sanitize(doTransliterate(filename))
}
if (info.agent.includes('Kindle')) {
filename = filename.replace(/[^\.\w\-"'\(\)]/g, '_')
}
if (mimetype === TYPE_EPUB && info.agent.includes('Kindle')) {
if (mimetype === TYPE_EPUB && info.agent.includes('Kindle') && ctx.request.body.kindlegen) {
// convert to .mobi
conversion = 'kindlegen'
const outname = ctx.request.file.path.replace(/\.epub$/i, '.mobi')
filename = filename.replace(/\.kepub\.epub$/i, '.epub').replace(/\.epub$/i, '.mobi')
let stderr = ''
data = await new Promise((resolve, reject) => {
let p = new Promise((resolve, reject) => {
const kindlegen = spawn('kindlegen', [basename(ctx.request.file.path), '-dont_append_source', '-c1', '-o', basename(outname)], {
stdio: 'inherit',
// stdio: 'inherit',
cwd: dirname(ctx.request.file.path)
})
kindlegen.once('error', function (err) {
fs.unlink(ctx.request.file.path, (err) => {
if (err) console.error(err)
else console.log('Removed file', ctx.request.file.path)
})
fs.unlink(ctx.request.file.path.replace(/\.epub$/i, '.mobi8'), (err) => {
if (err) console.error(err)
else console.log('Removed file', ctx.request.file.path.replace(/\.epub$/i, '.mobi8'))
})
reject('kindlegen error: ' + err)
})
kindlegen.once('close', (code) => {
fs.unlink(ctx.request.file.path, (err) => {
if (err) console.error(err)
@@ -281,64 +344,134 @@ router.post('/upload', upload.single('file'), async ctx => {
if (err) console.error(err)
else console.log('Removed file', ctx.request.file.path.replace(/\.epub$/i, '.mobi8'))
})
if (code !== 0) {
console.warn('kindlegen error code ' + code)
if (code !== 0 && code !== 1) {
reject('kindlegen error code: ' + code + '\n' + stderr)
return
}
resolve(outname)
})
kindlegen.stdout.on('data', function (str) {
stderr += str
console.log('kindlegen: ' + str)
})
kindlegen.stderr.on('data', function (str) {
stderr += str
console.log('kindlegen: ' + str)
})
})
try {
data = await p
} catch (err) {
flash(ctx, {
success: false,
message: err.replaceAll(basename(ctx.request.file.path), "infile.epub").replaceAll(basename(outname), "outfile.mobi")
})
return
}
} else if (mimetype === TYPE_EPUB && info.agent.includes('Kobo') && ctx.request.body.kepubify) {
} else if (mimetype === TYPE_EPUB && (info.agent.includes('Kobo') || info.agent.toLowerCase().includes('tolino')) && ctx.request.body.kepubify) {
// convert to Kobo EPUB
conversion = 'kepubify'
const outname = ctx.request.file.path.replace(/\.epub$/i, '.kepub.epub')
filename = filename.replace(/\.kepub\.epub$/i, '.epub').replace(/\.epub$/i, '.kepub.epub')
data = await new Promise((resolve, reject) => {
let p = new Promise((resolve, reject) => {
let stderr = ''
const kepubify = spawn('kepubify', ['-v', '-u', '-o', basename(outname), basename(ctx.request.file.path)], {
stdio: 'inherit',
//stdio: 'inherit',
cwd: dirname(ctx.request.file.path)
})
kepubify.once('error', function (err) {
fs.unlink(ctx.request.file.path, (err) => {
if (err) console.error(err)
else console.log('Removed file', ctx.request.file.path)
})
reject('kepubify error: ' + err)
})
kepubify.once('close', (code) => {
fs.unlink(ctx.request.file.path, (err) => {
if (err) console.error(err)
else console.log('Removed file', ctx.request.file.path)
})
if (code !== 0) {
reject('kepubify error code ' + code)
reject('Kepubify error code: ' + code + '\n' + stderr)
return
}
resolve(outname)
})
kepubify.stdout.on('data', function (str) {
stderr += str
console.log('kepubify: ' + str)
})
kepubify.stderr.on('data', function (str) {
stderr += str
console.log('kepubify: ' + str)
})
})
} else if (mimetype === 'application/pdf' && ctx.request.body.pdfCropMargins) {
try {
data = await p
} catch (err) {
flash(ctx, {
success: false,
message: err.replaceAll(basename(ctx.request.file.path), "infile.epub").replaceAll(basename(outname), "outfile.kepub.epub")
})
return
}
data = await new Promise((resolve, reject) => {
const pdfcropmargins = spawn('pdfcropmargins', ['-s', '-u', basename(ctx.request.file.path)], {
stdio: 'inherit',
} else if (mimetype == 'application/pdf' && ctx.request.body.pdfcropmargins) {
const dir = dirname(ctx.request.file.path)
const base = basename(ctx.request.file.path, '.pdf')
const outfile = resolvepath(join(dir, `${base}_cropped.pdf`))
let p = new Promise((resolve, reject) => {
let stderr = ''
const pdfcropmargins = spawn('pdfcropmargins', ['-s', '-u', '-o', outfile, basename(ctx.request.file.path)], {
// stdio: 'inherit',
cwd: dirname(ctx.request.file.path)
})
pdfcropmargins.once('error', function (err) {
fs.unlink(ctx.request.file.path, (err) => {
if (err) console.error(err)
else console.log('Removed file', ctx.request.file.path)
})
reject('pdfcropmargins error: ' + err)
})
pdfcropmargins.once('close', (code) => {
fs.unlink(ctx.request.file.path, (err) => {
if (err) console.error(err)
else console.log('Removed file', ctx.request.file.path)
})
if (code !== 0) {
reject('pdfcropmargins error code ' + code)
reject('pdfcropmargins error code: ' + code + '\n' + stderr)
return
}
const dir = dirname(ctx.request.file.path);
const base = basename(ctx.request.file.path, '.pdf');
resolve(join(dir, `${base}_cropped.pdf`))
resolve(outfile)
})
pdfcropmargins.stdout.on('data', function (str) {
stderr += str
console.log('pdfcropmargins: ' + str)
})
pdfcropmargins.stderr.on('data', function (str) {
stderr += str
console.log('pdfcropmargins: ' + str)
})
})
try {
data = await p
} catch (err) {
flash(ctx, {
success: false,
message: err.replaceAll(basename(ctx.request.file.path), "infile.pdf").replaceAll(outfile, "outfile.pdf")
})
return
}
} else {
// No conversion
data = ctx.request.file.path
filename = filename.replace(/\.epub$/i, '.epub').replace(/\.pdf$/i, '.pdf')
}
expireKey(key)
@@ -359,7 +492,8 @@ router.post('/upload', upload.single('file'), async ctx => {
let messages = []
if (ctx.request.file) {
messages.push('Upload successful! ' + (conversion ? ' Ebook was converted with ' + conversion + ' and sent' : ' Sent')+' to '+(info.agent.includes('Kobo') ? 'a Kobo device.' : (info.agent.includes('Kindle') ? 'a Kindle device.' : 'a device.')))
ctx.request.file.skip = true
messages.push('Upload successful! ' + (conversion ? 'Ebook was converted with ' + conversion + ' and sent' : 'Sent')+' to '+(info.agent.includes('Kobo') ? 'a Kobo device.' : (info.agent.includes('Kindle') ? 'a Kindle device.' : 'a device.')))
messages.push('Filename: ' + filename)
}
if (url) {
@@ -372,7 +506,7 @@ router.post('/upload', upload.single('file'), async ctx => {
success: false,
key: key
})
ctx.redirect('back', '/')
await next()
return
}
@@ -382,7 +516,8 @@ router.post('/upload', upload.single('file'), async ctx => {
key: key,
url: url
})
ctx.redirect('back', '/')
await next()
})
router.delete('/file/:key', async ctx => {
@@ -399,6 +534,7 @@ router.get('/status/:key', async ctx => {
const key = ctx.params.key.toUpperCase()
const info = ctx.keys.get(key)
if (!info) {
ctx.response.status = 404
ctx.body = {error: 'Unknown key'}
return
}
@@ -408,6 +544,7 @@ router.get('/status/:key', async ctx => {
return
}
expireKey(key)
ctx.cookies.set('key', key, {overwrite: true, httpOnly: false, sameSite: 'strict', maxAge: expireDelay * 1000})
ctx.body = {
alive: info.alive,
file: info.file ? {
@@ -425,19 +562,28 @@ router.get('/receive', async ctx => {
router.get('/', async ctx => {
const agent = ctx.get('user-agent')
console.log(ctx.ip, agent)
await sendfile(ctx, agent.includes('Kobo') || agent.includes('Kindle')? 'static/download.html' : 'static/upload.html')
await sendfile(ctx, agent.includes('Kobo') || agent.includes('Kindle') || agent.toLowerCase().includes('tolino') ? 'static/download.html' : 'static/upload.html')
})
router.get('/:filename', downloadFile)
app.use(serve("static"))
app.use(router.routes())
app.use(router.allowedMethods())
app.use(serve("static"))
fs.rm('uploads', {recursive: true}, (err) => {
if (err) throw err
mkdirp('uploads').then (() => {
app.listen(port)
// app.listen(port)
const fn = app.callback()
const server = http.createServer(fn)
server.on('checkContinue', (req, res) => {
console.log("check continue!")
fn(req, res)
})
server.listen(port)
console.log('server is listening on port ' + port)
})
})

1355
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,8 @@
"description": "Send ebooks to Kobo/Kindle ereaders easily",
"main": "index.js",
"scripts": {
"start": "node index"
"start": "node index",
"postinstall": "patch-package"
},
"author": "djazz",
"license": "MIT",
@@ -12,13 +13,14 @@
"@koa/multer": "^3.0.2",
"@koa/router": "^12.0.1",
"file-type": "^16.5.4",
"koa": "^2.15.0",
"koa": "^2.15.3",
"koa-logger": "^3.2.1",
"koa-mount": "^4.0.0",
"koa-sendfile": "^3.0.0",
"koa-static": "^5.0.0",
"mkdirp": "^3.0.1",
"multer": "^1.4.5-lts.1",
"patch-package": "^8.0.0",
"sanitize-filename": "^1.6.3",
"transliteration": "^2.3.5"
}
}

View File

@@ -0,0 +1,15 @@
diff --git a/node_modules/@koa/multer/index.js b/node_modules/@koa/multer/index.js
index d5be076..252208e 100644
--- a/node_modules/@koa/multer/index.js
+++ b/node_modules/@koa/multer/index.js
@@ -11,9 +11,7 @@
* Module dependencies.
*/
-let originalMulter = require('fix-esm').require('multer');
-
-if (originalMulter.default) originalMulter = originalMulter.default;
+let originalMulter = require('multer');
function multer(options) {
const m = originalMulter(options);

View File

@@ -78,12 +78,10 @@ function pollFile () {
downloads.style.display = 'none'
}
var urls = data.urls && data.urls.length > 0 ? data.urls : null
if (data.file || urls) {
} else {
}
if (data.file) {
downloads.style.display = 'block'
downloadlink.textContent = data.file.name
downloadlink.href = '/' + encodeURIComponent(data.file.name)
} else {
downloads.style.display = 'none'
}
@@ -111,7 +109,7 @@ function generateKey () {
if (x.responseText !== 'error' && x.status === 200) {
key = x.responseText
keyOutput.textContent = key
downloadlink.href = './download/' + key
downloadlink.href = ''
if (pollTimer) clearInterval(pollTimer)
pollTimer = setInterval(pollFile, 5 * 1000)
} else {

View File

@@ -104,6 +104,7 @@ input[type="url"], input[type="text"] {
#uploadstatus.error {
background-color: #FDD;
border: 1px solid #F77;
white-space: pre;
}
td {
padding: 10px;

View File

@@ -11,27 +11,28 @@
<div class="wrapper">
<h1 class="center">Send to Kobo/Kindle</h1>
<form action="./upload" method="post" enctype="multipart/form-data">
<form action="./upload" method="post" enctype="multipart/form-data" id="uploadform">
<table style="margin: 0 auto;" cellpadding=0 cellspacing=0>
<tr><td class="right"><label for="keyinput"><strong>Unique key</strong></label></td><td><input type="text" name="key" id="keyinput" autocomplete="off" pattern="...." placeholder="" required style="text-transform: uppercase;" maxlength=4/></td></tr>
<tr><td class="right aligntop"><label for="fileinput"><strong>Ebook file</strong></label><br/><em>EPUB, MOBI, PDF,<br/>TXT, CBZ, CBR</em></td><td class="aligntop"><label for="fileinput" id="choosebtn">Choose file</label><input type="file" name="file" id="fileinput" accept=".txt,.epub,.mobi,.pdf,.cbz,.cbr,application/epub+zip,application/epub,application/x-mobipocket-ebook,application/pdf,application/vnd.comicbook+zip,application/vnd.comicbook-rar"/><br/><br/><div id="fileinfo"></div></td></tr>
<tr><td class="right"><label for="urlinput"><strong>Send url</strong></label></td><td><input type="url" name="url" id="urlinput" autocomplete="off" style="width: 100%"></td></tr>
<tr><td class="right"><label for="kepubify"><strong>Kepubify</strong><br/><em>Kobo only</em></label></td><td><input type="checkbox" name="kepubify" id="kepubify" checked /></td></tr>
<tr><td class="right"><label for="kepubify"><strong>pdfCropMargins</strong><br/><em>PDF files only</em></label></td><td><input type="checkbox" name="pdfCropMargins" id="pdfCropMargins" checked /></td></tr>
<tr><td class="right"><input type="checkbox" name="kepubify" id="kepubify" checked /></td><td><label for="kepubify"><strong>Kepubify</strong> <em>Kobo only</em></label></td></tr>
<tr><td class="right"><input type="checkbox" name="kindlegen" id="kindlegen" checked /></td><td><label for="kindlegen"><strong>KindleGen</strong> <em>Kindle only</em></label></td></tr>
<tr><td class="right"><input type="checkbox" name="pdfcropmargins" id="pdfcropmargins" /></td><td><label for="pdfcropmargins"><strong>Crop margins of pdfs</strong></label></td></tr>
<tr><td class="right"><input type="checkbox" name="transliteration" id="transliteration" /></td><td><label for="transliteration"><strong>Transliteration of filename</strong></label></td></tr>
<tr class="center"><td colspan="2"><input type="submit" value="Upload and send" /></td></tr>
</table>
<div id="uploadstatus"></div>
</form>
<div style="padding: 15px; padding-top: 0; text-align: justify;">
<p>Go this this page on your Kobo/Kindle ereader and you see a unique key. Enter it in this form and upload an ebook and it will appear as a download link on the ereader.</p>
<p>If you send an EPUB file to to a Kindle it will be converted to MOBI with KindleGen. If you send a MOBI file to a Kindle it will be sent unprocessed. If you send an EPUB file and tick the Kepubify checkbox, it will be converted into a Kobo EPUB using Kepubify. If you send a MOBI file to a Kobo, it will not be converted.</p>
<p>If you send an EPUB file to to a Kindle it will be converted to MOBI with KindleGen. If you send a MOBI file to a Kindle it will be sent unprocessed. Files sent to Kindle eReaders will have their names stripped of special characters, a limitation of the Kindle browser. If you send an EPUB file and tick the Kepubify checkbox, it will be converted into a Kobo EPUB using Kepubify. If you send a MOBI file to a Kobo, it will not be converted.</p>
<p>Your ebook will be stored on the server as long as your Kobo/Kindle is viewing the unique key and is connected to wifi. It will be deleted irrevocably when the key expires about 30 seconds after you close the browser, generate a new key or disable wifi on your ereader.</p>
<p>By using this tool you agree that the ebook you upload is processed on the server and stored for a short time.</p>
</div>
<hr/>
<div class="center">
Created by djazz. Powered by <a href="https://koajs.com/" target="_blank">Koa</a>, <a href="https://pgaskin.net/kepubify/" target="_blank">Kepubify</a>, <a href="https://github.com/abarker/pdfCropMargins" target="_blank">pdfCropMargins</a> and <a href="https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211" target="_blank">KindleGen</a><br/>
Created by djazz. Powered by <a href="https://koajs.com/" target="_blank">Koa</a>, <a href="https://pgaskin.net/kepubify/" target="_blank">Kepubify</a>, <a href="https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211" target="_blank">KindleGen</a> and <a href="https://github.com/abarker/pdfCropMargins" target="_blank">pdfCropMargins</a><br/>
Source code on <a href="https://github.com/daniel-j/send2ereader" target="_blank">Github</a> - <a id="siteurl">https://send.djazz.se</span>
</div>
</div>
@@ -39,6 +40,7 @@
<div id="logs"></div>
<script>
var uploadform = document.getElementById('uploadform')
var uploadstatus = document.getElementById('uploadstatus')
var keyinput = document.getElementById('keyinput')
var fileinput = document.getElementById('fileinput')
@@ -47,34 +49,49 @@ var fileinfo = document.getElementById('fileinfo')
var urlinput = document.getElementById('urlinput')
var siteurl = document.getElementById('siteurl')
var flash = getCookies().flash
// deleteCookie('flash')
var flashtimer = null
if (flash) {
if (flash.message) {
if (flash.success) {
uploadstatus.className = " success"
uploadstatus.innerHTML = flash.message
} else {
uploadstatus.className = " error"
uploadstatus.textContent = flash.message
}
uploadstatus.style.opacity = 1
}
keyinput.value = flash.key || ''
urlinput.value = flash.url || ''
}
uploadstatus.addEventListener('click', function () {
function hideUploadStatus() {
uploadstatus.style.opacity = 0
setTimeout(function () {
clearTimeout(flashtimer)
flashtimer = setTimeout(function () {
uploadstatus.textContent = ''
uploadstatus.className = ''
}, 500)
}
function handleFlash(flash) {
// if (!flash) getCookies().flash
console.log(flash)
clearTimeout(flashtimer)
if (flash) {
if (flash.message) {
if (flash.success) {
uploadstatus.className = " success"
uploadstatus.innerHTML = flash.message.trim()
} else {
uploadstatus.className = " error"
uploadstatus.textContent = flash.message.trim()
}
uploadstatus.style.opacity = 1
}
if (flash.key) {
keyinput.value = flash.key
}
if (flash.url) {
urlinput.value = flash.url
}
} else {
hideUploadStatus()
}
}
// handleFlash()
uploadstatus.addEventListener('click', function () {
hideUploadStatus()
}, false)
document.body.addEventListener("drop", function () {
log("file dropped")
}, false)
function fileinputChange () {
if (!fileinput.files[0] || fileinput.files.length === 0) {
@@ -116,6 +133,50 @@ if (isIOS) {
fileinput.accept = ''
}
uploadform.addEventListener('submit', function (e) {
hideUploadStatus()
e.preventDefault()
var fd = new FormData(uploadform)
var req = new XMLHttpRequest()
req.open('POST', uploadform.action, true)
req.upload.onprogress = function (e) {
if (e.lengthComputable) {
console.log("progress: " + e.loaded / e.total)
var complete = e.loaded / e.total == 1
handleFlash({
success: true,
message: !complete ? "Uploading file... " + Math.round(100 * e.loaded / e.total) + '%' : "Processing uploaded file... please wait"
})
}
}
req.onreadystatechange = function (e) {
console.log(req.readyState, req.status)
}
req.onload = function () {
console.log('upload ok', req.status, req.responseText, req.responseType)
handleFlash({
success: req.status == 200,
message: req.responseText
})
}
req.onerror = function () {
console.log('upload error', req.status, req.responseText, req.responseType)
handleFlash({
success: false,
message: "Upload error - is the key correct?"
})
}
req.onabort = function () {
console.log('aborted', req.status)
handleFlash({
success: false,
message: "Upload aborted"
})
}
req.send(fd)
return false
}, false)
siteurl.textContent = window.location.href
siteurl.href = siteurl.textContent
siteurl.target = '_self'