Compare commits
10 commits
47e535b9e6
...
01ae1d7e92
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01ae1d7e92 | ||
|
|
c661d09926 | ||
|
|
44d5da95fe | ||
|
|
baf51cb651 | ||
|
|
27d59283a3 | ||
|
|
ef640752cc | ||
|
|
378f37c9b9 | ||
|
|
27ad48d389 | ||
|
|
98608ca168 | ||
|
|
6923da1dc0 |
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1 +1,3 @@
|
|||
.env
|
||||
.venv/
|
||||
__pycache__
|
||||
|
|
@ -91,6 +91,8 @@ This will first check if you already install gunicorn in the `.venv` folder, and
|
|||
|
||||
## Which browser to use?
|
||||
|
||||
> **WARNING**: these warnings sound so official.
|
||||
|
||||
> **WARNING**: When working collectively, it's recommended to use the same browser
|
||||
> and if possible also the same version of that browser, to get the same (or at least
|
||||
> the most similar) PDF. Different operating systems and browsers render CSS rules
|
||||
|
|
|
|||
18
filter_registry.py
Normal file
18
filter_registry.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# This is an attempt to create a customizable way to
|
||||
# add filters to the content coming from the etherpad
|
||||
# get_text api endpoint
|
||||
# in pad_filters.py do this:
|
||||
#
|
||||
# import re
|
||||
# from filter_registry import register_filter
|
||||
|
||||
# @register_filter
|
||||
# def remove_star_before_img(text):
|
||||
# return re.sub(r'\*\s*!?\[\]\(', '
|
||||
|
||||
pad_content_filters = []
|
||||
|
||||
def register_filter(func):
|
||||
print(f"Registering filter: {func.__name__}")
|
||||
pad_content_filters.append(func)
|
||||
return func
|
||||
152
octomode.py
152
octomode.py
|
|
@ -3,6 +3,7 @@ import json
|
|||
from flask import Flask, request, render_template, redirect, url_for
|
||||
from urllib.request import urlopen
|
||||
from urllib.parse import urlencode
|
||||
import html
|
||||
|
||||
# To sanitize Flask input fields
|
||||
from markupsafe import Markup, escape
|
||||
|
|
@ -14,11 +15,23 @@ import pypandoc
|
|||
# To read the Markdown metadat
|
||||
import markdown
|
||||
|
||||
|
||||
from filter_registry import pad_content_filters
|
||||
import pad_filters
|
||||
|
||||
|
||||
APP = Flask(__name__)
|
||||
APP.config.from_pyfile('settings.py')
|
||||
|
||||
# ---
|
||||
|
||||
# my attempt as showing helpful error messages.
|
||||
class MarkdownRenderError(RuntimeError):
|
||||
def __init__(self, kind, message, original):
|
||||
super().__init__(message)
|
||||
self.kind = kind
|
||||
self.original = original
|
||||
|
||||
def get_pad_content(pad_name, ext=""):
|
||||
if ext:
|
||||
pad_name = f'{ pad_name }{ ext }'
|
||||
|
|
@ -40,6 +53,11 @@ def get_pad_content(pad_name, ext=""):
|
|||
response = json.load(urlopen(f"{ APP.config['PAD_API_URL'] }/{ api_call }", data=urlencode(arguments).encode()))
|
||||
|
||||
content = response['data']['text']
|
||||
# print("before: " + content)
|
||||
print( "GET PAD CONTENT" )
|
||||
for f in pad_content_filters:
|
||||
content = f(content)
|
||||
# print("after: " + content)
|
||||
return content
|
||||
|
||||
def all_pads():
|
||||
|
|
@ -51,6 +69,22 @@ def all_pads():
|
|||
|
||||
return response
|
||||
|
||||
# get all pads that end in .md
|
||||
def all_publications():
|
||||
pads = all_pads()
|
||||
pubs = []
|
||||
print(pads)
|
||||
if(pads and pads['data'] and pads['data']['padIDs']):
|
||||
for pad in pads['data']['padIDs']:
|
||||
# if extension is .md add it to pubs
|
||||
if pad.endswith('.md'):
|
||||
# strip the .md
|
||||
name = pad.removesuffix('.md')
|
||||
pubs.append(name)
|
||||
return pubs
|
||||
else:
|
||||
return []
|
||||
|
||||
def create_pad_on_first_run(name, ext):
|
||||
pads = all_pads()
|
||||
pad = name+ext
|
||||
|
|
@ -76,7 +110,21 @@ def create_pad_on_first_run(name, ext):
|
|||
def md_to_html(md_pad_content):
|
||||
# Convert Markdown to HTML
|
||||
# html = markdown.markdown(md_pad_content, extensions=['meta', 'attr_list']) # attr_list does not work
|
||||
html = pypandoc.convert_text(md_pad_content, 'html', format='md')
|
||||
try:
|
||||
html = pypandoc.convert_text(md_pad_content, 'html', format='md')
|
||||
except RuntimeError as exc:
|
||||
message = str(exc)
|
||||
if "YAML" in message or "YAML metadata" in message:
|
||||
raise MarkdownRenderError(
|
||||
kind="yaml",
|
||||
message="Invalid YAML metadata. Use `key: value` and add a blank line.",
|
||||
original=str(exc)
|
||||
) from exc
|
||||
raise MarkdownRenderError(
|
||||
kind="markdown",
|
||||
message = "Markdown conversion failed. Please check the markdown for errors.",
|
||||
original = str(exc)
|
||||
) from exc
|
||||
|
||||
# Sanitize the Markdown
|
||||
# html = bleach.clean(html)
|
||||
|
|
@ -95,6 +143,36 @@ def get_md_metadata(md_pad_content):
|
|||
|
||||
return metadata
|
||||
|
||||
def get_app_root():
|
||||
if APP.config['APPLICATION_ROOT'] == '/':
|
||||
app_root = ''
|
||||
elif APP.config['APPLICATION_ROOT'].endswith('/'):
|
||||
app_root = APP.config['APPLICATION_ROOT'][:-1]
|
||||
else:
|
||||
app_root = APP.config['APPLICATION_ROOT']
|
||||
return app_root
|
||||
|
||||
|
||||
# def apply_cover(html_str, cover):
|
||||
# import html
|
||||
# html_str = str(html_str)
|
||||
# html_str = html_str.replace('class="cover"', "class='cover' style='background-image: url("+ cover +")'")
|
||||
# return Markup(html_str)
|
||||
|
||||
def get_meta(metadata, key, default=None):
|
||||
return metadata.get(key, [default])[0]
|
||||
|
||||
def render_markdown_error(name, error):
|
||||
print("ERROR", error)
|
||||
return render_template(
|
||||
'error.html',
|
||||
name=name.strip(),
|
||||
error_text=str(error),
|
||||
error=error,
|
||||
lang="en",
|
||||
title="Markdown error"
|
||||
), 400
|
||||
|
||||
# ---
|
||||
|
||||
@APP.route('/', methods=['GET', 'POST'])
|
||||
|
|
@ -110,7 +188,8 @@ def index():
|
|||
create_pad_on_first_run(name, ext)
|
||||
return redirect(url_for("pad", name=name))
|
||||
else:
|
||||
return render_template('start.html', pad_url=APP.config['PAD_URL'])
|
||||
pubs = all_publications()
|
||||
return render_template('start.html', pubs = pubs, home_pad_url=APP.config['HOME_PAD_URL'])
|
||||
|
||||
@APP.route('/<name>/')
|
||||
def main(name):
|
||||
|
|
@ -128,28 +207,22 @@ def stylesheet(name):
|
|||
|
||||
@APP.route('/<name>/html/')
|
||||
def html(name):
|
||||
# only here we need application root to make all the URLs work.....
|
||||
if APP.config['APPLICATION_ROOT'] == '/':
|
||||
app_root = ''
|
||||
elif APP.config['APPLICATION_ROOT'].endswith('/'):
|
||||
app_root = APP.config['APPLICATION_ROOT'][:-1]
|
||||
else:
|
||||
app_root = APP.config['APPLICATION_ROOT']
|
||||
app_root = get_app_root()
|
||||
url = f"{ app_root }/{ name }/preview.html"
|
||||
return render_template('iframe.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL'])
|
||||
|
||||
@APP.route('/<name>/pdf/')
|
||||
def pdf(name):
|
||||
# only here we need application root to make all the URLs work.....
|
||||
if APP.config['APPLICATION_ROOT'] == '/':
|
||||
app_root = ''
|
||||
elif APP.config['APPLICATION_ROOT'].endswith('/'):
|
||||
app_root = APP.config['APPLICATION_ROOT'][:-1]
|
||||
else:
|
||||
app_root = APP.config['APPLICATION_ROOT']
|
||||
app_root = get_app_root()
|
||||
url = f"{ app_root }/{name}/pagedjs.html"
|
||||
return render_template('pdf.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL'])
|
||||
|
||||
@APP.route('/<name>/impose/')
|
||||
def impose(name):
|
||||
app_root = get_app_root()
|
||||
url = f"{ app_root }/{name}/imposed.html"
|
||||
return render_template('pdf.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL'])
|
||||
|
||||
# //////////////////
|
||||
# RENDERED RESOURCES
|
||||
# //////////////////
|
||||
|
|
@ -166,31 +239,54 @@ def css(name):
|
|||
def preview(name):
|
||||
# TO GENERATE THE PREVIEW WEBPAGE
|
||||
md_pad_content = get_pad_content(name, ext='.md')
|
||||
html = md_to_html(md_pad_content)
|
||||
try:
|
||||
html = md_to_html(md_pad_content)
|
||||
except MarkdownRenderError as exc:
|
||||
return render_markdown_error(name, exc)
|
||||
metadata = get_md_metadata(md_pad_content)
|
||||
if metadata:
|
||||
lang = metadata['language'][0]
|
||||
title = metadata['title'][0]
|
||||
else:
|
||||
lang = "en"
|
||||
title = "No title"
|
||||
|
||||
lang = get_meta(metadata, 'language', 'en')
|
||||
title = get_meta(metadata, 'title', 'Untitled')
|
||||
|
||||
return render_template('preview.html', name=name.strip(), pad_content=html, lang=lang, title=title)
|
||||
|
||||
@APP.route('/<name>/pagedjs.html')
|
||||
def pagedjs(name):
|
||||
# TO GENERATE THE PAGED.JS WEBPAGE
|
||||
md_pad_content = get_pad_content(name, ext='.md')
|
||||
html = md_to_html(md_pad_content)
|
||||
try:
|
||||
html = md_to_html(md_pad_content)
|
||||
except MarkdownRenderError as exc:
|
||||
return render_markdown_error(name, exc)
|
||||
metadata = get_md_metadata(md_pad_content)
|
||||
lang = metadata['language'][0]
|
||||
title = metadata['title'][0]
|
||||
lang = get_meta(metadata, 'language', 'en')
|
||||
title = get_meta(metadata, 'title', 'Untitled')
|
||||
cover = get_meta(metadata, 'cover', None)
|
||||
|
||||
return render_template('pagedjs.html', name=name.strip(), pad_content=html, lang=lang, title=title)
|
||||
impose = False #request.args.get("impose") == "true"
|
||||
return render_template('pagedjs.html', name=name.strip(), pad_content=html, lang=lang, title=title, cover=cover, impose=impose)
|
||||
|
||||
@APP.route('/<name>/imposed.html')
|
||||
def imposed(name):
|
||||
# TO GENERATE THE IMPOSED WEBPAGE
|
||||
md_pad_content = get_pad_content(name, ext='.md')
|
||||
try:
|
||||
html = md_to_html(md_pad_content)
|
||||
except MarkdownRenderError as exc:
|
||||
return render_markdown_error(name, exc)
|
||||
metadata = get_md_metadata(md_pad_content)
|
||||
lang = get_meta(metadata, 'language', 'en')
|
||||
title = get_meta(metadata, 'title', 'Untitled')
|
||||
cover = get_meta(metadata, 'cover', None)
|
||||
|
||||
impose = True #request.args.get("impose") == "true"
|
||||
|
||||
return render_template('pagedjs.html', name=name.strip(), pad_content=html, lang=lang, title=title, cover=cover, impose=impose)
|
||||
|
||||
# //////////////////
|
||||
|
||||
if __name__ == '__main__':
|
||||
APP.debug = True
|
||||
APP.env = "development"
|
||||
# APP.debug = False
|
||||
# APP.env = "production"
|
||||
APP.run(host="0.0.0.0", port=APP.config["PORTNUMBER"], threaded=True)
|
||||
|
|
|
|||
16
pad_filters.py
Normal file
16
pad_filters.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import re
|
||||
from filter_registry import register_filter
|
||||
|
||||
@register_filter
|
||||
def remove_star_before_img(text):
|
||||
# regex version
|
||||
# return re.sub(r'\*\s*!?\[\]\(', '
|
||||
return re.sub(r'\*\s*!?\[\.*]\(', '
|
||||
|
||||
# @register_filter
|
||||
# def remove_star_before_img(text):
|
||||
# # print(">>> filter running")
|
||||
# # print(">>> before:", repr(text))
|
||||
# text = text.replace('*
|
||||
# # print(">>> after:", repr(text))
|
||||
# return text
|
||||
|
|
@ -1,14 +1,17 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from the .env file
|
||||
load_dotenv()
|
||||
# Load environment variables from the .env file alongside this module.
|
||||
ENV_PATH = Path(__file__).resolve().parent / ".env"
|
||||
load_dotenv(dotenv_path=ENV_PATH)
|
||||
|
||||
# Bind them to Python variables
|
||||
APPLICATION_ROOT = os.environ.get('OCTOMODE_APPLICATION_ROOT', '/')
|
||||
PORTNUMBER = int(os.environ.get('OCTOMODE_PORTNUMBER', 5001))
|
||||
PAD_URL = os.environ.get('OCTOMODE_PAD_URL', 'https://pad.vvvvvvaria.org')
|
||||
PAD_API_URL = os.environ.get('OCTOMODE_PAD_API_URL', 'https://pad.vvvvvvaria.org/api/1.2.15')
|
||||
HOME_PAD_URL = os.environ.get('OCTOMODE_HOME_PAD_URL', '')
|
||||
PAD_API_KEY = os.environ.get('OCTOMODE_PAD_API_KEY', '')
|
||||
|
||||
# Check if API key is provided
|
||||
|
|
|
|||
49
static/footnotes.js
Normal file
49
static/footnotes.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Adds a custom css property that switches footnote style
|
||||
between endnodes (default for Pandoc) and footnotes
|
||||
modified from: https://pagedjs.org/plugins/endnotes-to-footnotes/
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
const endNoteCalloutsQuery = ".footnote-ref";
|
||||
|
||||
// the hook
|
||||
class endToFootNotes extends Paged.Handler {
|
||||
constructor(chunker, polisher, caller) {
|
||||
super(chunker, polisher, caller);
|
||||
this.notestyle = 'endnotes';
|
||||
}
|
||||
|
||||
onDeclaration(declaration, dItem, dList, rule) {
|
||||
if (declaration.property == "--paged-note-style") {
|
||||
if (declaration.value.value.includes("footnote")) {
|
||||
console.log('FOOTNOTES!');
|
||||
// console.log(declaration.property, declaration, rule);
|
||||
this.notestyle = 'footnotes'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beforeParsed(content) {
|
||||
if ('footnotes' !== this.notestyle) {
|
||||
return;
|
||||
}
|
||||
console.log("parsef");
|
||||
let callouts = content.querySelectorAll(endNoteCalloutsQuery);
|
||||
callouts.forEach((callout) => {
|
||||
console.log(callout.hash)
|
||||
// console.log(callout.href)
|
||||
// console.log(`#${callout.href.callout.href.hash}`)
|
||||
let note = content.querySelector(callout.hash);
|
||||
console.log(note);
|
||||
if (!note) { return console.warn(`there is no note with the id of ${callout.hash}`) }
|
||||
let noteContent = `<span class="pagedjs-end-to-footnote">${note.innerHTML}</span>`;
|
||||
callout.insertAdjacentHTML("afterend", noteContent);
|
||||
callout.remove();
|
||||
note.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
Paged.registerHandlers(endToFootNotes);
|
||||
428
static/imposition.js
Normal file
428
static/imposition.js
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
// Imposition for booklet(s)
|
||||
//
|
||||
// This script re-arrange the pages of your document in order to make an imposed sheet layouts for printing.
|
||||
// Two pages per sheet, double-sided
|
||||
// modified from: https://gitlab.com/pagedjs-plugins/booklet-imposition/-/blob/master/imposition.js?ref_type=heads
|
||||
|
||||
class Booklet extends Paged.Handler {
|
||||
constructor(chunker, polisher, caller) {
|
||||
super(chunker, polisher, caller);
|
||||
this.pagedbooklet;
|
||||
this.sourceSize;
|
||||
this.pageStart;
|
||||
this.pageEnd;
|
||||
}
|
||||
onAtPage(node, item, list) { }
|
||||
onDeclaration(declaration, dItem, dList, rule) {
|
||||
if (declaration.property == "--paged-layout") {
|
||||
if (declaration.value.value.includes("booklet")) {
|
||||
console.log('BOOKLET');
|
||||
// console.log(declaration.property, declaration, rule);
|
||||
this.pagedbooklet = true;
|
||||
let valuesBooklet = declaration.value.value.trim().split(/\s+/);
|
||||
let index = valuesBooklet.indexOf("booklet");
|
||||
/* Set first page of the imposition */
|
||||
const rawStart = valuesBooklet[index + 1];
|
||||
if (rawStart) {
|
||||
const parsedStart = parseInt(rawStart, 10);
|
||||
this.pageStart = Number.isNaN(parsedStart) ? 1 : parsedStart;
|
||||
} else {
|
||||
this.pageStart = 1;
|
||||
}
|
||||
|
||||
/* Set last page of the imposition */
|
||||
const rawEnd = valuesBooklet[index + 2];
|
||||
if (rawEnd) {
|
||||
const parsedEnd = parseInt(rawEnd, 10);
|
||||
this.pageEnd = Number.isNaN(parsedEnd) ? undefined : parsedEnd;
|
||||
} else {
|
||||
this.pageEnd = undefined;
|
||||
}
|
||||
console.log(`START: ${this.pageStart} END: ${this.pageEnd}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
afterRendered(pages) {
|
||||
|
||||
|
||||
/* Verify this.pageEnd */
|
||||
if (!this.pageEnd) {
|
||||
let allPagesBefore = document.querySelectorAll(".pagedjs_page").length;
|
||||
this.pageEnd = allPagesBefore;
|
||||
}
|
||||
|
||||
/* Verify this.pageStart */
|
||||
if (this.pageStart == 0) {
|
||||
this.pageStart = 1;
|
||||
} else if (this.pageStart % 2 == 0) {
|
||||
this.pageStart = this.pageStart - 1;
|
||||
}
|
||||
|
||||
|
||||
/* Launch when printing */
|
||||
//window.addEventListener("beforeprint", (evenement) => {
|
||||
const reorder = (pages) => {
|
||||
let containerPages = document.querySelector(".pagedjs_pages");
|
||||
|
||||
|
||||
/* Delete pages we don't want*/
|
||||
pages.forEach(page => {
|
||||
let id = parseInt(page.id.replace('page-', ''));
|
||||
if (id < this.pageStart || id > this.pageEnd) {
|
||||
let pageSelect = document.querySelector('#' + page.id);
|
||||
pageSelect.remove();
|
||||
}
|
||||
});
|
||||
|
||||
/* Reset page counter */
|
||||
let reset = parseInt(this.pageStart) - 1;
|
||||
containerPages.style.counterReset = "page " + reset;
|
||||
|
||||
|
||||
let format = document.querySelector(".pagedjs_page");
|
||||
|
||||
/* Width of page without bleed, extract the first number of calc() function */
|
||||
let width = getCSSCustomProp("--pagedjs-width", format);
|
||||
let numbers = width
|
||||
.match(/[0-9]+/g)
|
||||
.map(function (n) {
|
||||
return + (n);
|
||||
});
|
||||
width = parseInt(numbers[0]);
|
||||
|
||||
/* Height of page with bleed, addition of all the numbers of calc() function*/
|
||||
let height = getCSSCustomProp("--pagedjs-height", format);
|
||||
numbers = height
|
||||
.match(/[0-9]+/g)
|
||||
.map(function (n) {
|
||||
return + (n);
|
||||
});
|
||||
const reducer = (previousValue, currentValue) => previousValue + currentValue;
|
||||
height = numbers.reduce(reducer);
|
||||
|
||||
/* Bleed of the page */
|
||||
let bleed = getCSSCustomProp("--pagedjs-bleed-top", format);
|
||||
let bleedNum = parseInt(bleed);
|
||||
|
||||
/* Spread and half-spread*/
|
||||
let spread = width * 2 + bleedNum * 2;
|
||||
|
||||
let spreadHalf = width + bleedNum;
|
||||
|
||||
// Add CSS to have pages in spread
|
||||
//
|
||||
// - change size of the page when printing (actually, sheet size)
|
||||
// - flex properties
|
||||
// - delete bleeds inside spread */
|
||||
|
||||
var newSize =
|
||||
`@media print{
|
||||
@page{
|
||||
size: ${spread}mm ${height}mm;
|
||||
}
|
||||
.pagedjs_pages {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
@media screen{
|
||||
.pagedjs_pages{
|
||||
max-width: calc(var(--pagedjs-width) * 2);
|
||||
}
|
||||
}
|
||||
.pagedjs_pages {
|
||||
display: flex !important;
|
||||
flex-wrap: wrap;
|
||||
transform: none !important;
|
||||
height: 100% !important;
|
||||
min-height: 100%;
|
||||
max-height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.pagedjs_page {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
height: 100% !important;
|
||||
|
||||
}
|
||||
|
||||
.pagedjs_sheet {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
height: 100% !important;
|
||||
}
|
||||
body{
|
||||
--pagedjs-bleed-right-left: 0mm;
|
||||
}
|
||||
.pagedjs_left_page{
|
||||
z-index: 20;
|
||||
width: calc(var(--pagedjs-bleed-left) + var(--pagedjs-pagebox-width))!important;
|
||||
}
|
||||
|
||||
.pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-crop {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.pagedjs_right_page,
|
||||
.pagedjs_right_page .pagedjs_sheet{
|
||||
width: calc(var(--pagedjs-bleed-right-right) + var(--pagedjs-pagebox-width))!important;
|
||||
}
|
||||
|
||||
.pagedjs_right_page .pagedjs_sheet{
|
||||
grid-template-columns: [bleed-left] var(--pagedjs-bleed-right-left) [sheet-center] 1fr [bleed-right] var(--pagedjs-bleed-right-right);
|
||||
}
|
||||
.pagedjs_right_page .pagedjs_bleed-left{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pagedjs_right_page .pagedjs_bleed-top .pagedjs_marks-crop:nth-child(1),
|
||||
.pagedjs_right_page .pagedjs_bleed-bottom .pagedjs_marks-crop:nth-child(1){
|
||||
width: 0!important;
|
||||
}
|
||||
.pagedjs_first_page {
|
||||
margin-left: 0;
|
||||
}
|
||||
body{
|
||||
margin: 0
|
||||
}
|
||||
.pagedjs_page:nth-of-type(even){
|
||||
break-after: always;
|
||||
}
|
||||
.pagedjs_page,
|
||||
.pagedjs_sheet{
|
||||
width: ${spreadHalf - 0.1}mm!important;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
// Add style for the arrangement of the pages
|
||||
|
||||
if (this.pagedbooklet == true) {
|
||||
let style = document.createElement("style");
|
||||
style.textContent = newSize;
|
||||
document
|
||||
.head
|
||||
.appendChild(style);
|
||||
|
||||
var number_of_pages = document.getElementsByClassName("pagedjs_page").length;
|
||||
var pages_array = [];
|
||||
|
||||
// If the page count isn't a multiple of 4, we need to pad the array with blank
|
||||
// pages so we have the correct number of pages for a booklet.
|
||||
//
|
||||
// ex. [1, 2, 3, 4, 5, 6, 7, 8, 9, blank, blank, blank]
|
||||
|
||||
let modulo = number_of_pages % 4;
|
||||
let additional_pages = 0;
|
||||
if (modulo != 0) {
|
||||
additional_pages = 4 - modulo;
|
||||
}
|
||||
|
||||
for (i = 0; i < additional_pages; i++) {
|
||||
let added_page = document.createElement("div");
|
||||
added_page
|
||||
.classList
|
||||
.add("pagedjs_page", "added");
|
||||
added_page.id = `page-${this.pageEnd + i + 1}`;
|
||||
added_page.innerHTML = `
|
||||
<div class="pagedjs_sheet">
|
||||
<div class="pagedjs_bleed pagedjs_bleed-top">
|
||||
<div class="pagedjs_marks-crop"></div>
|
||||
<div class="pagedjs_marks-middle">
|
||||
<div class="pagedjs_marks-cross"></div>
|
||||
</div>
|
||||
<div class="pagedjs_marks-crop"></div>
|
||||
</div>
|
||||
<div class="pagedjs_bleed pagedjs_bleed-bottom">
|
||||
<div class="pagedjs_marks-crop"></div>
|
||||
<div class="pagedjs_marks-middle">
|
||||
<div class="pagedjs_marks-cross"></div>
|
||||
</div> <div class="pagedjs_marks-crop"></div>
|
||||
</div>
|
||||
<div class="pagedjs_bleed pagedjs_bleed-left">
|
||||
<div class="pagedjs_marks-crop"></div>
|
||||
<div class="pagedjs_marks-middle">
|
||||
<div class="pagedjs_marks-cross"></div>
|
||||
</div> <div class="pagedjs_marks-crop"></div>
|
||||
</div>
|
||||
<div class="pagedjs_bleed pagedjs_bleed-right">
|
||||
<div class="pagedjs_marks-crop"></div>
|
||||
<div class="pagedjs_marks-middle">
|
||||
<div class="pagedjs_marks-cross"></div>
|
||||
</div>
|
||||
<div class="pagedjs_marks-crop"></div>
|
||||
</div>
|
||||
<div class="pagedjs_pagebox">
|
||||
<div class="pagedjs_margin-top-left-corner-holder">
|
||||
<div class="pagedjs_margin pagedjs_margin-top-left-corner"><div class="pagedjs_margin-content"></div></div>
|
||||
</div>
|
||||
<div class="pagedjs_margin-top">
|
||||
<div class="pagedjs_margin pagedjs_margin-top-left"><div class="pagedjs_margin-content"></div></div>
|
||||
<div class="pagedjs_margin pagedjs_margin-top-center"><div class="pagedjs_margin-content"></div></div>
|
||||
<div class="pagedjs_margin pagedjs_margin-top-right"><div class="pagedjs_margin-content"></div></div>
|
||||
</div>
|
||||
<div class="pagedjs_margin-top-right-corner-holder">
|
||||
<div class="pagedjs_margin pagedjs_margin-top-right-corner"><div class="pagedjs_margin-content"></div></div>
|
||||
</div>
|
||||
<div class="pagedjs_margin-right">
|
||||
<div class="pagedjs_margin pagedjs_margin-right-top"><div class="pagedjs_margin-content"></div></div>
|
||||
<div class="pagedjs_margin pagedjs_margin-right-middle"><div class="pagedjs_margin-content"></div></div>
|
||||
<div class="pagedjs_margin pagedjs_margin-right-bottom"><div class="pagedjs_margin-content"></div></div>
|
||||
</div>
|
||||
<div class="pagedjs_margin-left">
|
||||
<div class="pagedjs_margin pagedjs_margin-left-top"><div class="pagedjs_margin-content"></div></div>
|
||||
<div class="pagedjs_margin pagedjs_margin-left-middle"><div class="pagedjs_margin-content"></div></div>
|
||||
<div class="pagedjs_margin pagedjs_margin-left-bottom"><div class="pagedjs_margin-content"></div></div>
|
||||
</div>
|
||||
<div class="pagedjs_margin-bottom-left-corner-holder">
|
||||
<div class="pagedjs_margin pagedjs_margin-bottom-left-corner"><div class="pagedjs_margin-content"></div></div>
|
||||
</div>
|
||||
<div class="pagedjs_margin-bottom" style="grid-template-columns: 0px 1fr 0px;">
|
||||
<div class="pagedjs_margin pagedjs_margin-bottom-left"><div class="pagedjs_margin-content"></div></div>
|
||||
<div class="pagedjs_margin pagedjs_margin-bottom-center hasContent"><div class="pagedjs_margin-content"></div></div>
|
||||
<div class="pagedjs_margin pagedjs_margin-bottom-right"><div class="pagedjs_margin-content"></div></div>
|
||||
</div>
|
||||
<div class="pagedjs_margin-bottom-right-corner-holder">
|
||||
<div class="pagedjs_margin pagedjs_margin-bottom-right-corner"><div class="pagedjs_margin-content"></div></div>
|
||||
</div>
|
||||
<div class="pagedjs_area">
|
||||
<div class="pagedjs_page_content"><div>
|
||||
</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document
|
||||
.querySelector(".pagedjs_pages")
|
||||
.appendChild(added_page);
|
||||
}
|
||||
|
||||
// Push each page in the array
|
||||
|
||||
for (var i = number_of_pages + additional_pages; i >= 1; i--) {
|
||||
pages_array.push(i);
|
||||
}
|
||||
|
||||
// get the last page here, and add a 'pagedjs_last_page' class
|
||||
// it's mixing functionality a bit, but i don't want to iterate
|
||||
// a second time over the pages
|
||||
|
||||
const pagesList = Array.from(document.querySelectorAll('.pagedjs_page'));
|
||||
pagesList.forEach(p => p.classList.remove('pagedjs_last_page'));
|
||||
const last = pagesList.sort((a, b) => (parseInt(a.style.order || 0) - parseInt(b.style.order || 0))).pop();
|
||||
console.log("last", last);
|
||||
if (last) last.classList.add('pagedjs_last_page');
|
||||
|
||||
|
||||
// Split the array in half
|
||||
//
|
||||
// ex. [1, 2, 3, 4, 5, 6], [7, 8, 9, blank, blank, blank]
|
||||
|
||||
var split_start = pages_array.length / 2;
|
||||
|
||||
var split_end = pages_array.length;
|
||||
|
||||
var first_array = pages_array.slice(0, split_start);
|
||||
var second_array = pages_array.slice(split_start, split_end);
|
||||
|
||||
// Reverse the second half of the array. This is the beginning of the back half
|
||||
// of the booklet (from the center fold, back to the outside last page)
|
||||
//
|
||||
// ex. [blank, blank, blank, 9, 8, 7]
|
||||
|
||||
var second_array_reversed = second_array.reverse();
|
||||
|
||||
// Zip the two arrays together in groups of 2 These will end up being each '2-up
|
||||
// side' of the final document So, the sub-array at index zero will be the first
|
||||
// side of physical page one and index 1 will be the back side. However, they
|
||||
// won't yet be in the proper order.
|
||||
//
|
||||
// ex. [[1, blank], [2, blank], [3, blank], [4, 9], [5, 8], [6, 7]]
|
||||
|
||||
var page_groups = [];
|
||||
for (var i = 0; i < first_array.length; i++) {
|
||||
page_groups[i] = [
|
||||
first_array[i], second_array_reversed[i]
|
||||
];
|
||||
}
|
||||
|
||||
// We need to reverse every other sub-array starting with the first side. This
|
||||
// is the final step of aligning our booklet pages in the order with which the
|
||||
// booklet gets printed and bound.
|
||||
//
|
||||
// ex. [[blank, 1], [2, blank], [blank, 3], [4, 9], [8, 5], [6, 7]] final_groups
|
||||
// = page_groups.each_with_index { |group, index| group.reverse! if (index %
|
||||
// 2).zero? }
|
||||
var final_groups = [];
|
||||
for (var i = 0; i < page_groups.length; i++) {
|
||||
var group = page_groups[i];
|
||||
if (i % 2 != 0) {
|
||||
final_groups[i] = page_groups[i].reverse();
|
||||
} else {
|
||||
final_groups[i] = page_groups[i];
|
||||
}
|
||||
}
|
||||
console.log("Final Imposition Order: " + final_groups);
|
||||
|
||||
var allPages = document.querySelectorAll(".pagedjs_page");
|
||||
|
||||
var final_flat = final_groups.flat();
|
||||
|
||||
final_flat.forEach((folio, i) => {
|
||||
folio = folio + reset;
|
||||
document
|
||||
.querySelector(`#page-${folio}`)
|
||||
.style
|
||||
.order = i;
|
||||
});
|
||||
}
|
||||
|
||||
}; // before print
|
||||
reorder(pages);
|
||||
}
|
||||
}
|
||||
Paged
|
||||
.registerHandlers(Booklet);
|
||||
|
||||
/**
|
||||
* Pass in an element and its CSS Custom Property that you want the value of.
|
||||
* Optionally, you can determine what datatype you get back.
|
||||
*
|
||||
* @param {String} propKey
|
||||
* @param {HTMLELement} element=document.documentElement
|
||||
* @param {String} castAs='string'
|
||||
* @returns {*}
|
||||
*/
|
||||
const getCSSCustomProp = (
|
||||
propKey,
|
||||
element = document.documentElement,
|
||||
castAs = "string"
|
||||
) => {
|
||||
let response = getComputedStyle(element).getPropertyValue(propKey);
|
||||
|
||||
// Tidy up the string if there's something to work with
|
||||
if (response.length) {
|
||||
response = response
|
||||
.replace(/\'|"/g, "")
|
||||
.trim();
|
||||
}
|
||||
|
||||
// Convert the response into a whatever type we wanted
|
||||
switch (castAs) {
|
||||
case "number":
|
||||
case "int":
|
||||
return parseInt(response, 10);
|
||||
case "float":
|
||||
return parseFloat(response, 10);
|
||||
case "boolean":
|
||||
case "bool":
|
||||
return response === "true" || response === "1";
|
||||
}
|
||||
|
||||
// Return the string response by default
|
||||
return response;
|
||||
};
|
||||
|
|
@ -98,3 +98,25 @@ div.pagedjs_pages{
|
|||
div#nav{
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
|
||||
.cols {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 3fr;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.home_pad_iframe {
|
||||
height: 75vh;
|
||||
/* margin: 5rem; */
|
||||
/* width: calc(100vw - 13rem); */
|
||||
border: 1px solid black;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.home_pad_iframe {
|
||||
margin: 1rem;
|
||||
width: 90vw;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -177,4 +177,4 @@
|
|||
.pagedjs_bleed-left .pagedjs_marks-crop:last-child,
|
||||
.pagedjs_bleed-right .pagedjs_marks-crop:last-child{
|
||||
box-shadow: 0px -1px 0px 0px var(--pagedjs-crop-shadow);
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +34,9 @@ window.addEventListener('load', function () {
|
|||
|
||||
<a href="{{ url_for('html', name=name) }}"><button>html</button></a>
|
||||
|
||||
<a href="{{ url_for('pdf', name=name) }}"><button>pdf</button></a>
|
||||
<a href="{{ url_for('pdf', name=name) }}"><button>layout</button></a>
|
||||
|
||||
<a href="{{ url_for('impose', name=name) }}"><button>impose</button></a>
|
||||
</div>`;
|
||||
|
||||
document.body.insertBefore(nav, document.body.firstChild);
|
||||
|
|
|
|||
|
|
@ -2,34 +2,126 @@
|
|||
|
||||
@page{
|
||||
size: A5;
|
||||
margin: 10mm 20mm 25mm 20mm;
|
||||
margin: 10mm 15mm 20mm 15mm;
|
||||
|
||||
@bottom-center{
|
||||
content: counter(page);
|
||||
font-family: monospace;
|
||||
font-family: abordage;
|
||||
font-size: 150%;
|
||||
}
|
||||
}
|
||||
|
||||
@page :first {
|
||||
@bottom-center {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: abordage;
|
||||
src: url(https://chatty-pub-files.hackersanddesigners.nl/files/2/99/nMPuQi6bsXWzzHQcKzByuHnk/abordage-regular.woff);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Director-Regular;
|
||||
src: url(https://chatty-pub-files.hackersanddesigners.nl/files/2/d2/T7cPNlPHYfJD7uGerSbUl2zH/Director-Regular.otf);
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: Latitude;
|
||||
src: url(https://chatty-pub-files.hackersanddesigners.nl/files/2/f8/52Q5ce2-rtPtoRRQpZrwp0X_/Latitude-Regular.otf);
|
||||
}
|
||||
|
||||
body{
|
||||
font-size: 12px;
|
||||
font-family: Latitude;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
color: #822b01;
|
||||
--paged-layout: booklet;
|
||||
}
|
||||
|
||||
/* ------------------------------------ cover */
|
||||
|
||||
@page:first{
|
||||
background-color: #f3c6ff;
|
||||
color: #822b01;
|
||||
@page:first {
|
||||
color: white;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
filter: hue-rotate(17deg);
|
||||
}
|
||||
section#cover{
|
||||
|
||||
.cover{
|
||||
break-after: always;
|
||||
font-family: abordage;
|
||||
margin-top: 50px;
|
||||
padding: 15px;
|
||||
background: #822b01;
|
||||
background-clip: border-box;
|
||||
|
||||
}
|
||||
section#cover h1#title{
|
||||
font-size: 300%;
|
||||
|
||||
.cover h1#title {
|
||||
font-family: abordage;
|
||||
font-size: 500%;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
.cover h2 {
|
||||
font-family: abordage;
|
||||
font-size: 200%;
|
||||
}
|
||||
|
||||
/* ------------------------------------ main */
|
||||
|
||||
section#main pre{
|
||||
color: magenta;
|
||||
|
||||
h1 {
|
||||
font-family: abordage;
|
||||
font-size: 500%;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
section#main pre{
|
||||
color: black;
|
||||
}
|
||||
|
||||
.main h2 {
|
||||
font-family: abordage;
|
||||
font-size: 180%;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
blockquote{
|
||||
margin-right: 0;
|
||||
font-family: abordage;
|
||||
font-size: 140%;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
/*
|
||||
Images are always grayscale in the main content and
|
||||
are alone on a page
|
||||
*/
|
||||
.main img {
|
||||
display: block;
|
||||
filter: grayscale(100%);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
page-break-after: always;
|
||||
page-break-before: always;
|
||||
}
|
||||
|
||||
/*
|
||||
*** alone on a line in markdown will be turned into a <hr>
|
||||
we use this a way to force a page break, and hide the hr itself.
|
||||
*/
|
||||
|
||||
hr {
|
||||
break-after: page;
|
||||
border: none;
|
||||
margin: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,76 +4,59 @@ language: en
|
|||
---
|
||||
|
||||
<!--
|
||||
|
||||
|
|
||||
__ __ _|_ __ _ _ _ __ __| _
|
||||
/ \_/ | / \_/ |/ |/ | / \_/ | |/
|
||||
\__/ \___/|_/\__/ | | |_/\__/ \_/|_/|__/
|
||||
|
||||
|
||||
This document is opened in octomode.
|
||||
This document is opened in octomode.
|
||||
|
||||
pad : all materials for the PDF are collected here (written in Markdown)
|
||||
stylesheet : all CSS rules for the PDF are collected here (written in CSS)
|
||||
html : render the structure of the lay out as a HTML (with PyPandoc)
|
||||
[note] this view does not render any styling!
|
||||
pdf : render the lay out as a PDF (with Paged.js)
|
||||
Octomode is a collective editing space for PDF making that uses Etherpad, Paged.js, and Flask. It was first developed by Varia, building on the knowledge and practices of many designers and developers who work with and contribute to open-source and web-to-print approaches to making publications.
|
||||
|
||||
https://git.vvvvvvaria.org/varia/octomode
|
||||
Please keep in mind:
|
||||
|
||||
|
||||
* This instance of octomode is hosted on the server of Hackers & Designer's and is subject to H&D's code of conduct: https://hackersanddesigners.nl/code-of-conduct
|
||||
|
||||
* The pads are not indexed by search engines, but anyone who knows the URL can read and change them.
|
||||
|
||||
* The contents of the pads are not encrypted, meaning that they are not private.
|
||||
|
||||
* We make our own backups, meaning the the contents of all pads sit on H&D's harddrives potentially indefinitely.
|
||||
|
||||
* We might not be able to respond to pad retrieval requests.
|
||||
|
||||
* H&D is a small collective with changing energies and availabilities, which might affect the availability of the pads
|
||||
|
||||
* If you rely on the content of these pads, please remember to make your own backups.
|
||||
|
||||
|
||||
Octomode is for everyone who enjoys working side by side and looks for ways to escape, divest, and boycott big tech companies that create unaffordable, inaccessible black box software and are complicit in the genocidal war on Palestine.
|
||||
|
||||
How it works:
|
||||
pad: all content for the PDF are collected here (written in Markdown)
|
||||
stylesheet: all CSS rules for the PDF are collected here (written in CSS)
|
||||
html: render the structure of the layout as a HTML (with PyPandoc)
|
||||
layout: render the layout as a PDF, single pages (with Paged.js)
|
||||
imposition: sort pages so they can be printed double sided, folded in the middle an stapled.
|
||||
#MD?: markdown cheatsheet
|
||||
https://git.vvvvvvaria.org/varia/octomode
|
||||
|
||||
-->
|
||||
<!--
|
||||
|
||||
v _____ v _____ _ _ v _____ v ____ ____ _ ____
|
||||
\| ___"|/ |_ " _| |'| |'| \| ___"|/v | _"\ v v| _"\ vv /"\ v | _"\
|
||||
| _|" V | | /| |_| |\ | _|" R \| |_) |/ \| |_) |/ \/ _ \/ /| | | |
|
||||
| |___ /| |\ v| A |v | |___ | _ < I | __/ / ___ \ v| |_| |\
|
||||
|_____| v |_|v |_| |_| |_____| |_| \_\ |_| A/_/ \_\ |____/ v
|
||||
<< >> _// \\_ // \\ << >> // \\_ ||>>_ \\ >> |||_
|
||||
(_V_) (_A_)(_R_) (_I_)(_A_) ("_)(__) (__) (__) (_P_)(_A_)_D_) (__) (__)
|
||||
|
||||
Welcome to the etherpad-lite instance hosted by Varia!
|
||||
You are most welcome to use it but please take note of the following things:
|
||||
|
||||
VISIBILITY:
|
||||
- The pads are not indexed by search engines, but anyone that knows its URL is welcome to read and edit it.
|
||||
|
||||
PRIVACY:
|
||||
- The contents of the pads are not encrypted, meaning that they are not private.
|
||||
- Anyone with access to the server has the possibility to see the content of your pads.
|
||||
|
||||
RETENTION:
|
||||
- We make our own backups, meaning the the contents of all pads sit on our harddrives potentially indefinitely.
|
||||
- Because the identity of a pad author cannot be confirmed, we don't respond to pad retrieval requests.
|
||||
|
||||
ACCESSIBILITY:
|
||||
- If you rely on the content of these pads, please remember to make your own backups.
|
||||
- The availability of the pads is subject to cosmic events, spilled drinks and personal energies.
|
||||
|
||||
CODE OF CONDUCT:
|
||||
- Both the physical and digital spaces of Varia are subject to our Code of Conduct <https://varia.zone/en/pages/code-of-conduct.html>
|
||||
|
||||
If you wish to publish a pad to the Varia etherdump <https://etherdump.vvvvvvaria.org/> add the magic word __ PUBLISH __ (remove the spaces between the word and __) to your pad.
|
||||
|
||||
-->
|
||||
|
||||
<section id="cover">
|
||||
# *in octomode* { #title }
|
||||
|
||||
|
||||
::: cover
|
||||
|
||||
</section>
|
||||
# Title
|
||||
## Author
|
||||
|
||||
<section id="main">
|
||||
Octomode is a collective editing space for PDF making, using Etherpad, Paged.js and Flask.
|
||||
Everything else that goes on the cover
|
||||
|
||||
Inspired by the non-centralised, tentacular cognition capabilities of the octopus, we imagined a space in which the artificial boundaries of writing and designing can be crossed; where writing, editing and designing can be done in one environment simultaneously, allowing the format to influence the matter and vice-versa.
|
||||
:::
|
||||
|
||||
|
||||
```
|
||||
Edit this text in the PAD view.
|
||||
Edit the styling in the STYLESHEET view.
|
||||
Preview the page in the HTML view.
|
||||
Render it on pages in the PDF view.
|
||||
```
|
||||
::: main
|
||||
|
||||
# Title
|
||||
## Author
|
||||
|
||||
Everything that goes into the zine
|
||||
|
||||
:::
|
||||
|
||||
</section>
|
||||
|
|
|
|||
35
templates/error.html
Normal file
35
templates/error.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ lang }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="{{ url_for('css', name=name) }}" rel="stylesheet" type="text/css" media="print">
|
||||
<title>{{ title }} - {{ name }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ title }}</h1>
|
||||
<p>The document "{{name}}" has an error.</p>
|
||||
{% if error.kind == "yaml" %}
|
||||
<p>It seems like that there's something wrong with the YAML at the top of your document. <br />
|
||||
It should be structured like this:
|
||||
</p>
|
||||
<pre>
|
||||
---
|
||||
title: My Title
|
||||
lang: en
|
||||
cover: https://example.com/image.jpg
|
||||
---
|
||||
</pre>
|
||||
<p>
|
||||
Make sure that there is a space between the ":" and the value.
|
||||
</p>
|
||||
{% endif %}
|
||||
{% elif error.kind == "markdown" %}
|
||||
There's something wrong with the markdown in your pad. Please check the document or revert some of your recent changes.
|
||||
{% endif %}
|
||||
<details>
|
||||
<summary>Details</summary>
|
||||
{{error.original}}
|
||||
</details>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -4,8 +4,22 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="{{ url_for('static', filename='paged.polyfill.js') }}" type="text/javascript"></script>
|
||||
<script src="{{ url_for('static', filename='footnotes.js') }}" type="text/javascript"></script>
|
||||
{% if impose %}
|
||||
<script src="{{ url_for('static', filename='imposition.js') }}" type="text/javascript" id="imposition_js"></script>
|
||||
<style>body{
|
||||
--paged-layout: booklet;
|
||||
}</style>
|
||||
{% endif %}
|
||||
{% if cover %}
|
||||
<style>
|
||||
.pagedjs_first_page {
|
||||
background-image: url({{cover}});
|
||||
}
|
||||
</style>
|
||||
{% endif %}
|
||||
<link href="{{ url_for('static', filename='pagedjs.css') }}" rel="stylesheet" type="text/css" media="screen">
|
||||
<link href="/{{ name }}/stylesheet.css" rel="stylesheet" type="text/css" media="print">
|
||||
<link href="{{ url_for('css', name=name) }}" rel="stylesheet" type="text/css" media="print">
|
||||
<title>{{ title }}</title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block head %}
|
||||
<title>hallo</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<iframe id="pdf" name="pdf" src="{{ url }}"></iframe>
|
||||
{% endblock %}
|
||||
|
|
@ -20,9 +24,12 @@ window.addEventListener('load', function () {
|
|||
var head = document.getElementsByTagName('head')[0];
|
||||
head.insertBefore(cssLink, head.firstChild);
|
||||
|
||||
// Insert the SAVE button
|
||||
const nav = document.getElementById('buttons');
|
||||
const save = '<a href="#"><button id="save" onClick="printPage()">save</button></a>';
|
||||
// insert the IMPOSE button
|
||||
// const impose = '<a href="#"><button id="impose" onClick="impose()">impose</button></a>';
|
||||
|
||||
// Insert the SAVE button
|
||||
const save = '<a href="#"><button id="save" onClick="printPage()" style="background-color: #66ee66;">save</button></a>';
|
||||
nav.innerHTML = nav.innerHTML + save;
|
||||
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="/{{ name }}/stylesheet.css" rel="stylesheet" type="text/css" media="screen">
|
||||
<link href="{{ url_for('css', name=name) }}" rel="stylesheet" type="text/css" media="screen">
|
||||
<title>{{ title }}</title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -4,10 +4,24 @@
|
|||
<meta charset="utf-8" />
|
||||
<title>octomode</title>
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='main.css') }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<style>
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body class="start-page">
|
||||
<form action="{{ url_for('index') }}" method="POST">
|
||||
<h1><input type="submit" value="open"> <input type="text" name="name"> <em class="octomode">in octomode</em></h1>
|
||||
</form>
|
||||
<div class="cols">
|
||||
<h2>Publications</h2>
|
||||
<p>Below a list of the publications on the server.</p>
|
||||
<ul>
|
||||
{% for pub in pubs %}
|
||||
<a href="{{ url_for('pdf',name=pub)}}">{{pub}}</a>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<iframe class="home_pad_iframe" src="{{ home_pad_url }}"></iframe>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in a new issue