Commit 0a28e253 authored by Fred Chasen's avatar Fred Chasen

Add index page

parent fb90904b
......@@ -393,13 +393,13 @@
"integrity": "sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc="
},
"es5-ext": {
"version": "0.10.46",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz",
"integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==",
"version": "0.10.49",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.49.tgz",
"integrity": "sha512-3NMEhi57E31qdzmYp2jwRArIUsj1HI/RxbQ4bgnSB+AIKIxsAmTiK83bYMifIcpWvEc3P1X30DhUKOqEtF/kvg==",
"requires": {
"es6-iterator": "~2.0.3",
"es6-symbol": "~3.1.1",
"next-tick": "1"
"next-tick": "^1.0.0"
}
},
"es6-iterator": {
......@@ -987,6 +987,26 @@
}
}
},
"koa-send": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.0.tgz",
"integrity": "sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ==",
"requires": {
"debug": "^3.1.0",
"http-errors": "^1.6.3",
"mz": "^2.7.0",
"resolve-path": "^1.4.0"
}
},
"koa-static": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/koa-static/-/koa-static-5.0.0.tgz",
"integrity": "sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==",
"requires": {
"debug": "^3.1.0",
"koa-send": "^5.0.0"
}
},
"koa-validate": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/koa-validate/-/koa-validate-1.0.7.tgz",
......@@ -1116,6 +1136,16 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"requires": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
"thenify-all": "^1.0.0"
}
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
......@@ -1136,6 +1166,11 @@
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.4.1.tgz",
"integrity": "sha512-P9UbpFK87NyqBZzUuDBDz4f6Yiys8xm8j7ACDbi6usvFm6KItklQUKjeoqTrYS/S1k6I8oaOC2YLLDr/gg26Mw=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
......@@ -1232,9 +1267,9 @@
}
},
"pagedjs-cli": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/pagedjs-cli/-/pagedjs-cli-0.0.5.tgz",
"integrity": "sha512-/CnzuIIQn1cJecF1blW5g2Pj7mfCNnbufN1p7Gvqc8BJx3zKU3EwfRl9aJkctkZzN6G3lErRwh0WKtZhFaBgDg==",
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/pagedjs-cli/-/pagedjs-cli-0.0.7.tgz",
"integrity": "sha512-WQkwN7fmep5289aM1HvO/+YLglt1dK32L21FsJYbXdHmfIAMsGd0UykbSYl7HUUfSOhXRjgz96a9hah0cblXmg==",
"requires": {
"commander": "^2.20.0",
"express": "^4.16.4",
......@@ -1438,6 +1473,28 @@
"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
"integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs="
},
"resolve-path": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz",
"integrity": "sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc=",
"requires": {
"http-errors": "~1.6.2",
"path-is-absolute": "1.0.1"
},
"dependencies": {
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.4.0 < 2"
}
}
}
},
"restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
......@@ -1568,6 +1625,22 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
},
"thenify": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz",
"integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=",
"requires": {
"any-promise": "^1.0.0"
}
},
"thenify-all": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
"requires": {
"thenify": ">= 3.1.0 < 4"
}
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
......
......@@ -12,6 +12,7 @@ const bodyParser = require('koa-bodyparser');
const requestLogger = require('koa-logger');
const validate = require('koa-validate');
const mount = require('koa-mount');
const serve = require('koa-static');
// const pdf = require('./pdf');
......@@ -39,13 +40,10 @@ app.use(bodyParser(
validate(app);
// app.use((ctx, next) => {
// ctx.printer = printer;
// next();
// });
app.use(router.routes())
app.use(serve('static/'));
let server = app.listen(PORT, () => {
console.warn(`Printer server listening on ${PORT}, in ${process.env.NODE_ENV} mode`);
});
......
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Paged.Press - Pagedjs PDF Generator</title>
<meta content="width=device-width, initial-scale=1" name="viewport">
<script src="scripts/download.js"></script>
<script src="scripts/main.js"></script>
<link rel="stylesheet" type="text/css" href="styles/main.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
</head>
<body>
<div id="main">
<div id="controls">
<header id="logo">
<h1>Paged.Press</h1>
<p>Paged.js PDF Generator</p>
</header>
<form id="form" class="box" method="post" action="" enctype="multipart/form-data">
<div class="box__input">
<i class="material-icons box__icon">file_download</i>
<input class="box__file" type="file" name="files[]" id="input" />
<label for="file"><a id="choose"><strong>Choose a HTML file</strong></a><span class="box__dragndrop"> or drag it here</span>.</label>
</div>
<div class="box__uploading">Uploading&hellip;</div>
<div class="box__success">Done!</div>
<div class="box__error">Error!<span></span>.</div>
</form>
<footer>
<h3>Print a URL</h3>
<pre class="prettyprint">
curl http://paged.press \
--header "Content-Type:application/json" \
--data '{"url": "https://s3.amazonaws.com/pagedmedia/samples/text.html"}' \
> out.pdf
</pre>
<h3>Print a local file</h3>
<pre class="prettyprint">
curl http://paged.press \
--header "Content-Type:text/plain" \
--data-binary "@text.html" > out.pdf
</pre>
<h3>Request a print with "fetch" in JavaScript</h3>
<pre class="prettyprint">
let file = await fetch('http://paged.press', {
method: 'POST',
body: JSON.stringify({
url: "https://s3.amazonaws.com/pagedmedia/samples/text.html"
// html: "..."
}),
headers: {
'Content-Type': 'application/json',
},
})
.then(r => r.blob());
</pre>
</footer>
</div>
<div id="viewer">
<div class="lds-spinner"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>
<iframe id="pdf" src="" type="application/pdf"></iframe>
</div>
<a id="close">- Close -</a>
</div>
</body>
</html>
//download.js v4.2, by dandavis; 2008-2016. [CCBY2] see http://danml.com/download.html for tests/usage
// v1 landed a FF+Chrome compat way of downloading strings to local un-named files, upgraded to use a hidden frame and optional mime
// v2 added named files via a[download], msSaveBlob, IE (10+) support, and window.URL support for larger+faster saves than dataURLs
// v3 added dataURL and Blob Input, bind-toggle arity, and legacy dataURL fallback was improved with force-download mime and base64 support. 3.1 improved safari handling.
// v4 adds AMD/UMD, commonJS, and plain browser support
// v4.1 adds url download capability via solo URL argument (same domain/CORS only)
// v4.2 adds semantic variable names, long (over 2MB) dataURL support, and hidden by default temp anchors
// https://github.com/rndme/download
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.download = factory();
}
}(this, function () {
return function download(data, strFileName, strMimeType) {
var self = window, // this script is only for browsers anyway...
defaultMime = "application/octet-stream", // this default mime also triggers iframe downloads
mimeType = strMimeType || defaultMime,
payload = data,
url = !strFileName && !strMimeType && payload,
anchor = document.createElement("a"),
toString = function(a){return String(a);},
myBlob = (self.Blob || self.MozBlob || self.WebKitBlob || toString),
fileName = strFileName || "download",
blob,
reader;
myBlob= myBlob.call ? myBlob.bind(self) : Blob ;
if(String(this)==="true"){ //reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback
payload=[payload, mimeType];
mimeType=payload[0];
payload=payload[1];
}
if(url && url.length< 2048){ // if no filename and no mime, assume a url was passed as the only argument
fileName = url.split("/").pop().split("?")[0];
anchor.href = url; // assign href prop to temp anchor
if(anchor.href.indexOf(url) !== -1){ // if the browser determines that it's a potentially valid url path:
var ajax=new XMLHttpRequest();
ajax.open( "GET", url, true);
ajax.responseType = 'blob';
ajax.onload= function(e){
download(e.target.response, fileName, defaultMime);
};
setTimeout(function(){ ajax.send();}, 0); // allows setting custom ajax headers using the return:
return ajax;
} // end if valid url?
} // end if url?
//go ahead and download dataURLs right away
if(/^data\:[\w+\-]+\/[\w+\-]+[,;]/.test(payload)){
if(payload.length > (1024*1024*1.999) && myBlob !== toString ){
payload=dataUrlToBlob(payload);
mimeType=payload.type || defaultMime;
}else{
return navigator.msSaveBlob ? // IE10 can't do a[download], only Blobs:
navigator.msSaveBlob(dataUrlToBlob(payload), fileName) :
saver(payload) ; // everyone else can save dataURLs un-processed
}
}//end if dataURL passed?
blob = payload instanceof myBlob ?
payload :
new myBlob([payload], {type: mimeType}) ;
function dataUrlToBlob(strUrl) {
var parts= strUrl.split(/[:;,]/),
type= parts[1],
decoder= parts[2] == "base64" ? atob : decodeURIComponent,
binData= decoder( parts.pop() ),
mx= binData.length,
i= 0,
uiArr= new Uint8Array(mx);
for(i;i<mx;++i) uiArr[i]= binData.charCodeAt(i);
return new myBlob([uiArr], {type: type});
}
function saver(url, winMode){
if ('download' in anchor) { //html5 A[download]
anchor.href = url;
anchor.setAttribute("download", fileName);
anchor.className = "download-js-link";
anchor.innerHTML = "downloading...";
anchor.style.display = "none";
document.body.appendChild(anchor);
setTimeout(function() {
anchor.click();
document.body.removeChild(anchor);
if(winMode===true){setTimeout(function(){ self.URL.revokeObjectURL(anchor.href);}, 250 );}
}, 66);
return true;
}
// handle non-a[download] safari as best we can:
if(/(Version)\/(\d+)\.(\d+)(?:\.(\d+))?.*Safari\//.test(navigator.userAgent)) {
url=url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
if(!window.open(url)){ // popup blocked, offer direct download:
if(confirm("Displaying New Document\n\nUse Save As... to download, then click back to return to this page.")){ location.href=url; }
}
return true;
}
//do iframe dataURL download (old ch+FF):
var f = document.createElement("iframe");
document.body.appendChild(f);
if(!winMode){ // force a mime that will download:
url="data:"+url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
}
f.src=url;
setTimeout(function(){ document.body.removeChild(f); }, 333);
}//end saver
if (navigator.msSaveBlob) { // IE10+ : (has Blob, but not a[download] or URL)
return navigator.msSaveBlob(blob, fileName);
}
if(self.URL){ // simple fast and modern way using Blob and URL:
saver(self.URL.createObjectURL(blob), true);
}else{
// handle non-Blob()+non-URL browsers:
if(typeof blob === "string" || blob.constructor===toString ){
try{
return saver( "data:" + mimeType + ";base64," + self.btoa(blob) );
}catch(y){
return saver( "data:" + mimeType + "," + encodeURIComponent(blob) );
}
}
// Blob but not URL support:
reader=new FileReader();
reader.onload=function(e){
saver(this.result);
};
reader.readAsDataURL(blob);
}
return true;
}; /* end download() */
}));
\ No newline at end of file
class Uploader {
constructor(onUpload) {
this.onUpload = onUpload;
this.form = document.getElementById("form");
this.form.addEventListener("dragenter", this.dragenter.bind(this), false);
this.form.addEventListener("dragover", this.dragover.bind(this), false);
this.form.addEventListener("dragleave", this.dragleave.bind(this), false);
this.form.addEventListener("drop", this.drop.bind(this), false);
this.inputListener();
}
dragenter(e) {
e.stopPropagation();
e.preventDefault();
this.form.classList.add("is-dragover");
}
dragover(e) {
e.stopPropagation();
e.preventDefault();
}
dragleave(e) {
e.stopPropagation();
e.preventDefault();
this.form.classList.remove("is-dragover");
}
drop(e) {
e.stopPropagation();
e.preventDefault();
var dt = e.dataTransfer;
var files = dt.files;
this.upload(files[0]);
}
inputListener() {
let inputElement = document.getElementById("input");
let choose = document.getElementById("choose");
choose.addEventListener("click", () => {
inputElement.click();
}, false);
inputElement.addEventListener('change', (e) => {
var file = e.target.files[0];
this.upload(file);
this.form.style.display = "none";
});
}
upload(file) {
if (window.FileReader) {
var reader = new FileReader();
reader.onload = this.result.bind(this);
reader.readAsArrayBuffer(file);
}
}
result(e) {
this.onUpload(e.target.result);
}
destroy() {
}
}
function getPdf(html) {
return fetch(window.location.origin, {
method: 'POST',
body: JSON.stringify({
html
}),
headers: {
'Content-Type': 'application/json',
},
})
.then(r => r.blob())
}
let ready = new Promise(function(resolve, reject){
if (document.readyState === "interactive" || document.readyState === "complete") {
resolve(document.readyState);
return;
}
document.onreadystatechange = function ($) {
if (document.readyState === "interactive") {
resolve(document.readyState);
}
}
});
ready.then(function () {
let uploader = new Uploader(async (data) => {
var dataView = new DataView(data);
var decoder = new TextDecoder("utf-8");
var decodedString = decoder.decode(dataView);
let controls = document.querySelector("#controls");
controls.style.display = "none";
let viewer = document.querySelector("#viewer");
viewer.style.display = "block";
let pdf = await getPdf(decodedString);
download(pdf, "output.pdf", "application/pdf");
let spinner = document.querySelector(".lds-spinner");
spinner.style.display = "none";
let iframe = document.querySelector("#pdf");
var file = window.URL.createObjectURL(pdf);
iframe.src = file;
let closer = document.querySelector("#close");
closer.style.display = "block";
closer.onclick = () => {
controls.style.display = "block";
viewer.style.display = "none";
spinner.style.display = "inline-block";
closer.style.display = "none";
iframe.src = "";
}
});
});
.annotator-frame, hypothesis-adder {
display: none;
}
/* hypothesis-highlight.annotator-hl {
background-color: transparent !important;;
text-decoration: underline;
} */
.annotator-highlights-always-on .annotator-hl {
background-color: transparent !important;
text-decoration-line: underline;
text-decoration-style: dashed;
}
@media screen {
#main {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
#navigation {
width: 500px;
position: fixed;
overflow: auto;
top: 0;
left: -525px;
background: #ECECEC;
min-height: 100%;
height: 100%;
height: 100vh;
overflow: scroll;
-webkit-overflow-scrolling: touch;
padding: 9px;
padding-top: 10px;
transition: left .2s ease-out;
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
z-index: 1;
}
#navigation.open {
left: 0;
}
#opener {
position: absolute;
top: 0;
left: 0;
padding: 10px;
stroke: #E2E2E2;
fill: #E2E2E2;
visibility: hidden;
}
#opener:hover {
stroke: #777;
fill: #777;
}
#header {
height: 30px;
}
footer {
text-align: left;
width: 700px;
margin: 0 auto;
}
#update {
float: right;
}
.CodeMirror {
border: 1px solid #eee;
height: calc(100vh - 24px);
box-sizing: border-box;
background: #fbfbfb;
box-shadow: inset 0 0 1px rgba(0,0,0,.8);
}
.material-icons {
color: #333;
cursor: pointer;
}
.material-icons:hover {
color: #525252;
}
.material-icons:active {
color: #000;
}
.pagedjs_page {
box-shadow: 0 0 4px #ccc;
}
.annotator-frame, hypothesis-adder {
display: unset;
}
}
:root {
--margin: 100px;
}
#opener, #controls, #next, #prev, #navigation {
display: none;
}
@media screen {
body {
background-color: whitesmoke;
}
#main {
display: block;
}
#opener, #controls, #next, #prev, #navigation {
display: unset;
}
#controls {
margin: 20px 0;
text-align: center;
}
}
.box {
font-size: 1.25rem; /* 20 */
background-color: #ddd;
position: relative;
padding: 100px 20px;
outline: 2px dashed #ccc;
outline-offset: -10px;
-webkit-transition: outline-offset .15s ease-in-out, background-color .15s linear;
transition: outline-offset .15s ease-in-out, background-color .15s linear;
width: 400px;
margin: 80px auto;
}
.box.is-dragover {
outline-offset: -20px;
outline-color: #000;
background-color: #ccc;
}
.box .box__dragndrop {
display: inline;
}
.box .box__icon {
width: 100%;
height: 80px;
fill: #92b0b3;
display: block;
margin-bottom: 40px;
font-size: 100px;
}
.box.is-uploading .box__input,
.box.is-success .box__input,
.box.is-error .box__input
{
visibility: hidden;
}
.box__uploading,