Skip to content

Instantly share code, notes, and snippets.

@ssddanbrown
Last active Feb 5, 2021
Embed
What would you like to do?
Get export-like HTML using the API
// The BASE_URL of your BookStack instance, no trailing slash
const baseUrl = 'http://bookstack.local';
//////////////////
// USAGE EXAMPLE
///
// Get the export content for page ID 4
/////////////////////////
/*
getPageExportHtml(4).then(exportHtml => {
console.log(exportHtml);
}).catch(error => console.log(error));
*/
//////////////////
/**
* Get the page export HTML.
* @param {Number} id
* @returns {String}
*/
async function getPageExportHtml(id) {
const page = await getPageFromApi(id);
return `<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>${page.name}</title>
<style>${getExportStyles()}</style>
</head>
<body>
<div id="page-show">
<div class="page-content">
<div dir="auto">
<h1 class="break-text" id="bkmrk-page-title">${page.name}</h1>
<div style="clear:left;"></div>
${page.html}
</div>
</div>
</div>
</body>
</html>`;
}
/**
* Get page data from the API, for the page of the given ID.
* @param {Number} id
* @returns {Promise<Object>}
*/
function getPageFromApi(id) {
return fetch(`${baseUrl}/api/pages/${id}`).then(res => res.json());
}
/**
* Get the styles used in the export HTML.
* @returns {String}
*/
function getExportStyles() {
return `:root {
--color-primary: #206ea7;
--color-primary-light: rgba(32,110,167,0.15);
--color-page: #206ea7;
--color-page-draft: #7e50b1;
--color-chapter: #af4d0d;
--color-book: #077b70;
--color-bookshelf: #a94747;
}
* {
box-sizing: border-box;
outline-color: var(--color-primary);
outline-width: 1px;
}
*:focus {
outline-style: dotted;
}
html {
height: 100%;
overflow-y: scroll;
background-color: #F2F2F2;
}
html.flexbox {
overflow-y: hidden;
}
html.dark-mode {
background-color: #111;
}
body {
font-size: 14px;
line-height: 1.6;
color: #444;
-webkit-font-smoothing: antialiased;
background-color: #F2F2F2;
height: 100%;
display: flex;
flex-direction: column;
}
html.dark-mode body {
color: #AAA;
}
/**
* Fonts
*/
body, button, input, select, label, textarea {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Roboto", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}
.Codemirror, pre, #markdown-editor-input, .editor-toolbar, .code-base, span.code, code {
font-family: "Lucida Console", "DejaVu Sans Mono", "Ubuntu Mono", Monaco, monospace;
}
/*
* Header Styles
*/
h1 {
font-size: 3.425em;
line-height: 1.22222222em;
margin-top: 0.48888889em;
margin-bottom: 0.48888889em;
}
h2 {
font-size: 2.8275em;
line-height: 1.294117647em;
margin-top: 0.8627451em;
margin-bottom: 0.43137255em;
}
h3 {
font-size: 2.333em;
line-height: 1.221428572em;
margin-top: 0.78571429em;
margin-bottom: 0.43137255em;
}
h4 {
font-size: 1.666em;
line-height: 1.375em;
margin-top: 0.78571429em;
margin-bottom: 0.43137255em;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 400;
position: relative;
display: block;
color: #222;
}
html.dark-mode h1, html.dark-mode h2, html.dark-mode h3, html.dark-mode h4, html.dark-mode h5, html.dark-mode h6 {
color: #BBB;
}
h1 .subheader, h2 .subheader, h3 .subheader, h4 .subheader, h5 .subheader, h6 .subheader {
font-size: 0.5em;
line-height: 1em;
color: #969696;
}
h5 {
font-size: 1.4em;
}
h5, h6 {
line-height: 1.2em;
margin-top: 0.78571429em;
margin-bottom: 0.66em;
}
@media screen and (max-width: 600px) {
h1 {
font-size: 2.8275em;
}
h2 {
font-size: 2.333em;
}
h3 {
font-size: 1.666em;
}
h4 {
font-size: 1.333em;
}
h5 {
font-size: 1.161616em;
}
}
.list-heading {
font-size: 2rem;
}
h2.list-heading {
font-size: 1.333rem;
}
/*
* Link styling
*/
a {
color: var(--color-primary);
fill: currentColor;
cursor: pointer;
text-decoration: none;
transition: filter ease-in-out 80ms;
line-height: 1.6;
}
a:hover {
text-decoration: underline;
}
a.icon {
display: inline-block;
}
a svg {
position: relative;
display: inline-block;
}
a:focus img:only-child {
outline: 2px dashed var(--color-primary);
outline-offset: 2px;
}
.blended-links a {
color: inherit;
}
.blended-links a svg {
fill: currentColor;
}
/*
* Other HTML Text Elements
*/
p, ul, ol, pre, table, blockquote {
margin-top: 0.3em;
margin-bottom: 1.375em;
}
hr {
border: 0;
height: 1px;
background: #eaeaea;
margin-bottom: 24px;
}
html.dark-mode hr {
background: #555;
}
hr.faded {
background-image: linear-gradient(to right, #FFF, #e3e0e0 20%, #e3e0e0 80%, #FFF);
}
hr.margin-top, hr.even {
margin-top: 24px;
}
strong, b, .bold, .strong {
font-weight: bold;
}
strong > strong, strong > b, strong > .bold, strong > .strong, b > strong, b > b, b > .bold, b > .strong, .bold > strong, .bold > b, .bold > .bold, .bold > .strong, .strong > strong, .strong > b, .strong > .bold, .strong > .strong {
font-weight: bolder;
}
em, i, .italic {
font-style: italic;
}
small, p.small, span.small, .text-small {
font-size: 0.75rem;
color: #5e5e5e;
}
html.dark-mode small, html.dark-mode p.small, html.dark-mode span.small, html.dark-mode .text-small {
color: #999;
}
sup, .superscript {
vertical-align: super;
font-size: 0.8em;
}
sub, .subscript {
vertical-align: sub;
font-size: 0.8em;
}
pre {
font-size: 12px;
border: 1px solid #DDD;
background-color: #f5f5f5;
border-color: #DDD;
padding-left: 31px;
position: relative;
padding-top: 3px;
padding-bottom: 3px;
}
html.dark-mode pre {
background-color: #2B2B2B;
}
html.dark-mode pre {
border-color: #111;
}
pre:after {
content: "";
display: block;
position: absolute;
top: 0;
width: 29px;
left: 0;
height: 100%;
background-color: #f5f5f5;
border-right: 1px solid #DDD;
}
html.dark-mode pre:after {
background-color: #313335;
}
html.dark-mode pre:after {
border-right: none;
}
@media print {
pre {
padding-left: 12px;
}
pre:after {
display: none;
}
}
blockquote {
display: block;
position: relative;
border-left: 4px solid var(--color-primary);
background-color: #f8f8f8;
padding: 12px 16px 12px 32px;
overflow: auto;
}
html.dark-mode blockquote {
background-color: #333;
}
blockquote:before {
content: "“";
font-size: 2em;
font-weight: bold;
position: absolute;
top: 12px;
left: 12px;
color: #777777;
}
.text-mono {
font-family: "Lucida Console", "DejaVu Sans Mono", "Ubuntu Mono", Monaco, monospace;
}
.text-uppercase {
text-transform: uppercase;
}
.text-capitals {
text-transform: capitalize;
}
.code-base, span.code, code {
font-size: 0.84em;
border: 1px solid #DDD;
border-radius: 3px;
background-color: #f8f8f8;
border-color: #DDD;
}
html.dark-mode .code-base, html.dark-mode span.code, html.dark-mode code {
background-color: #2b2b2b;
}
html.dark-mode .code-base, html.dark-mode span.code, html.dark-mode code {
border-color: #444;
}
code {
display: inline;
padding: 1px 3px;
white-space: pre-wrap;
line-height: 1.2em;
}
span.code {
padding: 1px 6px;
}
pre code {
background-color: transparent;
border: 0;
font-size: 1em;
display: block;
line-height: 1.6;
}
span.highlight {
font-weight: bold;
padding: 2px 4px;
}
/*
* Lists
*/
ul p, ol p {
margin: 0;
}
ul {
padding-left: 20.8px;
padding-right: 20.8px;
list-style: disc;
}
ul ul {
list-style: circle;
margin-top: 0;
margin-bottom: 0;
}
ul label {
margin: 0;
}
ol {
list-style: decimal;
padding-left: 32px;
padding-right: 32px;
}
li.checkbox-item, li.task-list-item {
list-style: none;
margin-left: -20.8px;
}
li.checkbox-item input[type=checkbox], li.task-list-item input[type=checkbox] {
margin-right: 6px;
}
li > ol, li > ul {
margin-block-end: 0px;
margin-block-start: 0px;
padding-block-end: 0px;
padding-block-start: 0px;
}
/*
* Generic text styling classes
*/
.underlined {
text-decoration: underline;
}
.text-center {
text-align: center;
}
.text-left {
text-align: start;
}
.text-right {
text-align: end;
}
.text-bigger {
font-size: 1.1em;
}
.text-large {
font-size: 1.6666em;
}
.no-color {
color: inherit;
}
.break-text {
word-wrap: break-word;
overflow-wrap: break-word;
}
/**
* Grouping
*/
span.sep {
color: #BBB;
padding: 0 6px;
}
.list > * {
display: block;
}
/**
* Generic content container
*/
.container {
max-width: 1400px;
margin-inline-start: auto;
margin-inline-end: auto;
padding-inline-start: 16px;
padding-inline-end: 16px;
}
.container.small {
max-width: 840px;
}
.container.very-small {
max-width: 480px;
}
.items-center {
align-items: center;
}
/**
* Display and float utilities
*/
.block {
display: block !important;
position: relative;
}
.inline {
display: inline !important;
}
.block.inline {
display: inline-block !important;
}
.hidden {
display: none !important;
}
.fill-height {
height: 100%;
}
.float {
float: left;
}
.float.right {
float: right;
}
/**
* Callouts
*/
.callout {
border-inline-start: 3px solid #BBB;
background-color: #EEE;
padding: 12px 12px 12px 32px;
display: block;
position: relative;
overflow: auto;
}
.callout:before {
background-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9IiMwMTUzODAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+ICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz4gICAgPHBhdGggZD0iTTEyIDJDNi40OCAyIDIgNi40OCAyIDEyczQuNDggMTAgMTAgMTAgMTAtNC40OCAxMC0xMFMxNy41MiAyIDEyIDJ6bTEgMTVoLTJ2LTZoMnY2em0wLThoLTJWN2gydjJ6Ii8+PC9zdmc+");
background-repeat: no-repeat;
content: "";
width: 1.2em;
height: 1.2em;
left: 8px;
top: 50%;
margin-top: -9px;
display: inline-block;
position: absolute;
line-height: 1;
opacity: 0.8;
}
.callout.success {
border-left-color: #0f7d15;
background-color: #eafdeb;
color: #063409;
}
html.dark-mode .callout.success {
background-color: #031904;
}
html.dark-mode .callout.success {
color: #129419;
}
.callout.success:before {
background-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9IiMzNzZjMzkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+ICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz4gICAgPHBhdGggZD0iTTEyIDJDNi40OCAyIDIgNi40OCAyIDEyczQuNDggMTAgMTAgMTAgMTAtNC40OCAxMC0xMFMxNy41MiAyIDEyIDJ6bS0yIDE1bC01LTUgMS40MS0xLjQxTDEwIDE0LjE3bDcuNTktNy41OUwxOSA4bC05IDl6Ii8+PC9zdmc+");
}
.callout.danger {
border-left-color: #ab0f0e;
background-color: #fcdbdb;
color: #4d0706;
}
html.dark-mode .callout.danger {
background-color: #1e0302;
}
html.dark-mode .callout.danger {
color: #c31110;
}
.callout.danger:before {
background-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9IiNiOTE4MTgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+ICAgIDxwYXRoIGQ9Ik0xNS43MyAzSDguMjdMMyA4LjI3djcuNDZMOC4yNyAyMWg3LjQ2TDIxIDE1LjczVjguMjdMMTUuNzMgM3pNMTIgMTcuM2MtLjcyIDAtMS4zLS41OC0xLjMtMS4zIDAtLjcyLjU4LTEuMyAxLjMtMS4zLjcyIDAgMS4zLjU4IDEuMyAxLjMgMCAuNzItLjU4IDEuMy0xLjMgMS4zem0xLTQuM2gtMlY3aDJ2NnoiLz4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPjwvc3ZnPg==");
}
.callout.info {
border-left-color: #0288D1;
color: #01466c;
background-color: #d3efff;
}
html.dark-mode .callout.info {
color: #09a7fd;
}
html.dark-mode .callout.info {
background-color: #001520;
}
.callout.warning {
border-left-color: #cf4d03;
background-color: #fee3d3;
color: #6a2802;
}
html.dark-mode .callout.warning {
background-color: #1a0a00;
}
html.dark-mode .callout.warning {
color: #cf4d03;
}
.callout.warning:before {
background-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9IiNiNjUzMWMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+ICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz4gICAgPHBhdGggZD0iTTEgMjFoMjJMMTIgMiAxIDIxem0xMi0zaC0ydi0yaDJ2MnptMC00aC0ydi00aDJ2NHoiLz48L3N2Zz4=");
}
.callout a {
color: inherit;
text-decoration: underline;
}
table {
min-width: 100px;
max-width: 100%;
}
table thead {
background-color: #f8f8f8;
font-weight: 500;
}
html.dark-mode table thead {
background-color: #333;
}
table td, table th {
min-width: 10px;
padding: 6px 8px;
border: 1px solid #DDD;
overflow: auto;
line-height: 1.2;
}
table td p, table th p {
margin: 0;
}
.page-content {
width: 100%;
max-width: 840px;
margin: 0 auto;
overflow-wrap: break-word;
}
.page-content .align-left {
text-align: left;
}
.page-content img.align-left, .page-content table.align-left {
float: left !important;
margin: 6px 16px 16px 0;
}
.page-content .align-right {
text-align: right !important;
}
.page-content img.align-right, .page-content table.align-right {
float: right !important;
margin: 6px 0 6px 12px;
}
.page-content .align-center {
text-align: center;
}
.page-content img.align-center {
display: block;
}
.page-content img.align-center, .page-content table.align-center {
margin-left: auto;
margin-right: auto;
}
.page-content img {
max-width: 100%;
height: auto;
}
.page-content h1, .page-content h2, .page-content h3, .page-content h4, .page-content h5, .page-content h6, .page-content pre {
clear: left;
}
.page-content hr {
clear: both;
margin: 16px 0;
}
.page-content table {
hyphens: auto;
table-layout: fixed;
max-width: 100%;
height: auto !important;
}
html, body {
background-color: #FFF;
}
body {
font-family: "DejaVu Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Roboto", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
margin: 0;
padding: 0;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
.page-content {
overflow: hidden;
}
pre {
padding-left: 12px;
}
pre:after {
display: none;
}
pre code {
white-space: pre-wrap;
}`;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment