Commit 81932ac6 authored by lukas.niegsch's avatar lukas.niegsch

translated old application version with renderjs

parent 224a4434
<!DOCTYPE html>
<html>
<head>
<title>Browser Gadget</title>
<script src="include/nexedi/rsvp.js"></script>
<script src="include/nexedi/jio.js"></script>
<script src="include/nexedi/renderjs.js"></script>
<script src="gadget_browser.js"></script>
</head>
</html>
\ No newline at end of file
(function(window, rJS, RSVP) {
"use strict";
var BROWSER_URL = "http://localhost:9222";
var BROWSER_USERNAME = "optional"
var BROWSER_PASSWORD = "optional"
rJS(window)
.declareMethod("makeBrowserRequest", function (url) {
return RSVP.Queue()
.push(function () {
return new RSVP.Promise(function (resolve, reject) {
var request = new XMLHttpRequest();
request.open("GET", BROWSER_URL + "/" + url, true,
BROWSER_USERNAME, BROWSER_PASSWORD);
request.withCredentials = true;
request.onload = () => {
var response = JSON.parse(request.responseText);
resolve(response);
}
request.onerror = (error) => {
reject("Could not connect to browser!");
}
request.send();
})
})
})
.declareMethod("getBrowserWebSocketUrl", function () {
var gadget = this;
return gadget.makeBrowserRequest("json/version")
.push(function (response) {
return response.webSocketDebuggerUrl;
})
})
.declareMethod("getPageWebsocketUrl", function (page) {
var gadget = this;
return gadget.makeBrowserRequest("json/list")
.push(function (response) {
for (const key in response) {
if (!response.hasOwnProperty(key)) {
continue;
}
var target = response[key];
if (target.id == page) {
return target.webSocketDebuggerUrl
}
}
})
})
.declareMethod("runCommand", function (websocket, command, params = {}) {
return RSVP.Queue()
.push(function () {
return new RSVP.Promise(function (resolve, reject) {
var server = new WebSocket(websocket);
server.onopen = () => {
var message = {id: 0, method: command, params: params}
server.send(JSON.stringify(message))
}
server.onmessage = (response) => {
server.close()
var data = JSON.parse(response.data)
if ('error' in data) reject(data.error)
if ('result' in data) resolve(data.result)
if ('method' in data) resolve(data.method)
reject(data)
}
server.onerror = (error) => {
server.close()
reject(error)
}
})
})
})
.declareMethod("runBrowserCommand", function (command, params = {}) {
var gadget = this;
return gadget.getBrowserWebSocketUrl()
.push(function (websocket) {
return gadget.runCommand(websocket, command, params);
})
})
.declareMethod("runPageCommand", function (page, command, params = {}) {
var gadget = this;
return gadget.getPageWebsocketUrl(page)
.push(function (websocket) {
return gadget.runCommand(websocket, command, params);
})
})
.declareMethod("getVersion", function () {
var gadget = this;
return gadget.runBrowserCommand("Browser.getVersion");
})
.declareMethod("openNewPage", function (url) {
var gadget = this;
return gadget.runBrowserCommand("Target.createTarget", {url: "" + url})
.push(function (response) {
return response.targetId;
})
})
.declareMethod("closePage", function (page) {
var gadget = this;
return gadget.runBrowserCommand("Target.closeTarget", {targetId: page});
})
.declareMethod("enablePage", function (page) {
var gadget = this;
return gadget.runPageCommand(page, "Page.enable");
})
.declareMethod("setPageContent", function (page, html) {
var gadget = this;
// TODO: implement this method
})
.declareMethod("printToPdf", function (page, options) {
var gadget = this;
return gadget.runPageCommand(page, "Page.printToPDF", options)
.push(function (result) {
return result.data;
});
})
}(window, rJS, RSVP));
\ No newline at end of file
<!-- todo: implement (empty) gadget that can convert html to pdf --> <!DOCTYPE html>
<!---idea: use this gadget inside erp5 later, but test it here as standalone --> <html>
\ No newline at end of file <head>
<title>Converter Gadget</title>
<script src="include/nexedi/rsvp.js"></script>
<script src="include/nexedi/jio.js"></script>
<script src="include/nexedi/renderjs.js"></script>
<script src="gadget_converter.js"></script>
</head>
<body>
<div
data-gadget-url="gadget_browser.html"
data-gadget-scope="browser"
data-gadget-sandbox="public">
</div>
</body>
</html>
\ No newline at end of file
(function(window, rJS) {
"use strict";
function mm2inches (mm)
{
return mm / 25.4
}
/*
Options for printing:
https://chromedevtools.github.io/devtools-protocol/1-3/Page/#method-printToPDF
*/
var optionsForPrintToPdf =
{
landscape: true,
displayHeaderFooter: false,
printBackground: true,
scale: 1,
paperWidth: mm2inches(297 /* millimetre */),
paperHeight: mm2inches(210 /* millimetre */),
marginTop: mm2inches(10 /* millimetre */),
marginBottom: mm2inches(10 /* millimetre */),
marginLeft: mm2inches(10 /* millimetre */),
marginRight: mm2inches(10 /* millimetre */),
pageRanges: "",
headerTemplate: "",
footerTemplate: "",
preferCSSPageSize: false
}
rJS(window)
.declareMethod("convert", function(html) {
var gadget = this;
var browser;
var page;
var base64data;
return gadget.getDeclaredGadget("browser")
.push(function (subgadget) {
browser = subgadget;
return browser.openNewPage("http://www.example.com");
})
.push(function (result) {
page = result;
return browser.setPageContent(page, html);
})
.push(function () {
return browser.enablePage(page);
})
.push(function () {
return browser.printToPdf(page, optionsForPrintToPdf);
})
.push(function (result) {
base64data = result;
return browser.closePage(page);
})
.push(function () {
return base64data;
})
})
}(window, rJS));
\ No newline at end of file
...@@ -2,12 +2,46 @@ html, body { ...@@ -2,12 +2,46 @@ html, body {
margin: 0px; margin: 0px;
height: 100%; height: 100%;
width: 100%; width: 100%;
display: flex;
overflow: hidden;
}
.left {
width: 50%;
height: 100%;
}
.right {
width: 50%;
height: 100%;
} }
.CodeMirror { .CodeMirror {
border-right: 1px solid rgb(0, 0, 0);
margin: 0, auto; margin: 0, auto;
width: 50%; width: 100%;
height: 100%; height: 100%;
font-size: 14px; font-size: 14px;
} }
#viewer {
margin: 0, auto;
border: 0;
padding: 0;
width: 100%;
height: 100%;
}
#convert {
position: fixed;
--width: 80px;
width: var(--width);
--height: 30px;
height: var(--height);
bottom: calc(0%);
right: calc(50% - var(--width) / 2);
background-color: white1;
text-align: center;
box-shadow: 2px 2px 3px #999;
font-size: 16px;
z-index: 9999;
}
\ No newline at end of file
...@@ -18,8 +18,13 @@ ...@@ -18,8 +18,13 @@
<script src="index.js"></script> <script src="index.js"></script>
</head> </head>
<body> <body>
<button class="convert" id="convert">Convert</button>
<div class="left">
<textarea id="editor"></textarea> <textarea id="editor"></textarea>
<div id="viewer" style="display: none;">Placeholder PDF Viewer</div> </div>
<div class="right">
<iframe id="viewer"></iframe>
</div>
<div <div
data-gadget-url="gadget_converter.html" data-gadget-url="gadget_converter.html"
data-gadget-scope="converter" data-gadget-scope="converter"
......
...@@ -23,6 +23,14 @@ ...@@ -23,6 +23,14 @@
}); });
return gadget.changeState({update: true}); return gadget.changeState({update: true});
}) })
.onEvent("click", function (event) {
var gadget = this;
switch (event.target.className.trim())
{
case "convert":
return gadget.changeState({update: true});
}
})
.onStateChange(function (ignored) { .onStateChange(function (ignored) {
var gadget = this; var gadget = this;
var editor = document.querySelector(".CodeMirror").CodeMirror; var editor = document.querySelector(".CodeMirror").CodeMirror;
...@@ -33,10 +41,13 @@ ...@@ -33,10 +41,13 @@
.push(function (subgadget) { .push(function (subgadget) {
converter = subgadget; converter = subgadget;
let html = editor.getValue(); let html = editor.getValue();
// return converter.convert(html); return converter.convert(html);
}) })
.push(function (base64data) { .push(function (base64data) {
// todo: implement this function + pdf viewer (maybe use PDF.js) viewer.src = "data:application/pdf;base64," + base64data;
})
.push(function () {
gadget.state.update = false;
}) })
}) })
}(window, rJS)); }(window, rJS));
\ No newline at end of file
class RequestHandler
{
constructor()
{
this.buffer = ["https://www.example.com", "https://www.nexedi.com/"]
}
hasNextRequest()
{
return this.buffer.length != 0
}
getNextRequest()
{
return this.buffer.pop()
}
publishResults(name, data)
{
var a = document.createElement("a");
a.href = "data:application/pdf;base64," + data;
a.download = `${name}.pdf`;
a.click();
}
}
class HeadlessChromium
{
constructor(url)
{
this.url = url
this.browserWebsocket = this.getBrowserWebsocketUrl()
this.pageWebsockets = new Map()
}
getBrowserWebsocketUrl()
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", this.url + "/json/version", false);
xmlHttp.send(null);
return JSON.parse(xmlHttp.responseText).webSocketDebuggerUrl
}
getPageWebsocket(page)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", this.url + "/json/list", false);
xmlHttp.send(null);
for (const target of JSON.parse(xmlHttp.responseText))
{
if (target.id = page)
{
return target.webSocketDebuggerUrl
}
}
}
async runCommand(websocket, command, params = {})
{
return new Promise((resolve, reject) => {
var server = new WebSocket(websocket)
server.onopen = () => {
var message = {id: 0, method: command, params: params}
server.send(JSON.stringify(message))
}
server.onmessage = (response) => {
server.close()
var data = JSON.parse(response.data)
if ('error' in data)
{
reject(data.error)
}
if ('result' in data)
{
resolve(data.result)
}
if ('method' in data)
{
console.log("event: " + data.method)
resolve(data.method)
}
reject(data)
}
server.onerror = (error) => {
server.close()
reject(error)
}
})
}
async runBrowserCommand(command, params = {})
{
return await this.runCommand(this.browserWebsocket, command, params)
}
async runPageCommand(page, command, params = {})
{
var websocket = this.pageWebsockets.get(page)
if (!websocket)
{
console.error(page + ' is not a valid page. Use browser.createPage() to create a valid page.')
return
}
return await this.runCommand(websocket, command, params)
}
async getVersion()
{
return await this.runBrowserCommand("Browser.getVersion")
}
async openNewPage(url, width = 1920, height = 1080)
{
var response = await this.runBrowserCommand("Target.createTarget", {url: url, width: width, height: height})
var page = response.targetId
this.pageWebsockets.set(page, this.getPageWebsocket(page))
return page
}
async closePage(page)
{
this.pageWebsockets.delete(page)
return await this.runBrowserCommand("Target.closeTarget", {targetId: page})
}
async enablePage(page)
{
return await this.runPageCommand(page, "Page.enable")
}
async printToPdf(page, options)
{
// wip: printing fails for some reason
var response = await this.runPageCommand(page, "Page.printToPDF", options)
return response.data //atob(response.data)
}
}
function mm2inches (mm)
{
return mm / 25.4
}
/*
Options for printing:
https://chromedevtools.github.io/devtools-protocol/1-3/Page/#method-printToPDF
*/
var optionsForPrintToPdf =
{
landscape: true,
displayHeaderFooter: false,
printBackground: true,
scale: 1,
paperWidth: mm2inches(297 /* millimetre */),
paperHeight: mm2inches(210 /* millimetre */),
marginTop: mm2inches(10 /* millimetre */),
marginBottom: mm2inches(10 /* millimetre */),
marginLeft: mm2inches(10 /* millimetre */),
marginRight: mm2inches(10 /* millimetre */),
pageRanges: "",
headerTemplate: "",
footerTemplate: "",
preferCSSPageSize: false
}
/*
Usage: Start google chromium with the following command line flags.
--headless
--remote-debugging-port=9222
--disable-web-security
--user-data-dir=/var/tmp/chrome
The last two flags can be ommited if we find another way to enable
Cross-Origin Resource Sharing. Then load this page with a browser
search param:
http://localhost:8080/?browser=http://localhost:9222
*/
var searchParams = (new URL(window.location.href)).searchParams
var browser = new HeadlessChromium(searchParams.get("browser"))
var handler = new RequestHandler()
async function html2pdf(url)
{
var page = await browser.openNewPage(url)
await browser.enablePage(page)
var pdf = await browser.printToPdf(page, optionsForPrintToPdf)
await browser.closePage(page)
return pdf
}
async function mainloop()
{
if (!handler.hasNextRequest())
{
setTimeout(mainloop, 30 /* seconds */ * 1000)
return
}
var url = handler.getNextRequest()
var pdf = await html2pdf(url)
var name = (new URL(url)).hostname
handler.publishResults(name, pdf)
setTimeout(mainloop, 3 /* seconds */ * 1000)
}
mainloop()
\ No newline at end of file
#! /bin/bash
CLIENT_PORT=8080
SERVER_PORT=9222
google-chrome-stable --headless --remote-debugging-port=${SERVER_PORT} &
python -m http.server -b localhost ${CLIENT_PORT} &
sleep 3
google-chrome-stable --disable-web-security --user-data-dir=/var/tmp/chrome "http://localhost:${CLIENT_PORT}?browser=http://localhost:${SERVER_PORT}"
#! /bin/bash
PORT=8080
# we require an headless chromium instance, e.g.
# google-chrome-stable --headless --remote-debugging-port=9222 &
# change the first few lines inside gadget_browser.js accordingly
# the browser does not have to run on localhost
# todo: allow users to change the browser parameters dynamically
# todo: inside standalone there might be a way to use the users browser instead
# todo: currently only shows page for http://www.example.com
# should be the typed html instead
python -m http.server -b localhost ${PORT} &
sleep 3
google-chrome-stable --disable-web-security --user-data-dir=/var/tmp/chrome "http://localhost:${PORT}"
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment