diff --git a/.htaccess b/.htaccess
index 3c8e810..e549d42 100644
--- a/.htaccess
+++ b/.htaccess
@@ -1 +1 @@
-FallbackResource /app.php
+FallbackResource /index.php
diff --git a/app.php b/app.php
deleted file mode 100644
index d5bef1b..0000000
--- a/app.php
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
diff --git a/assets/html2opendocument b/assets/html2opendocument
deleted file mode 160000
index 864f014..0000000
--- a/assets/html2opendocument
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 864f014cf1f048a25911c3db361c5796e9f9648f
diff --git a/assets/htmlpurifier b/assets/htmlpurifier
deleted file mode 160000
index 6d6d885..0000000
--- a/assets/htmlpurifier
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 6d6d88512a8146939a9161bb03e95e3e97840439
diff --git a/assets/parsedown b/assets/parsedown
deleted file mode 160000
index c999a4b..0000000
--- a/assets/parsedown
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit c999a4b61bfc15d3eed74a40ce814afe6faa21ea
diff --git a/assets/parsedown-extra b/assets/parsedown-extra
deleted file mode 160000
index 0db5cce..0000000
--- a/assets/parsedown-extra
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 0db5cce7354e4b76f155d092ab5eb3981c21258c
diff --git a/assets/spyc b/assets/spyc
deleted file mode 160000
index 576c42e..0000000
--- a/assets/spyc
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 576c42ecbc9630b52a95da3279f07278572c6b15
diff --git a/config.php b/config.php
index a90c400..63d71b4 100644
--- a/config.php
+++ b/config.php
@@ -1,7 +1,19 @@
array(
+ 'name' => "Framapad",
+ 'url' => "https://annuel2.framapad.org",
+ 'default_text' => "–––––",
+ ),
+ 'board' => array(
+ 'name' => "Board",
+ 'url' => "https://board.net",
+ 'default_text' => "--",
+ )
+);
+
+$options = array(
+ 'name' => "Libreto",
+ 'default_provider' => "framapad",
+ 'providers' => $providers,
+);
diff --git a/index.php b/index.php
index c2e12a7..6a34011 100644
--- a/index.php
+++ b/index.php
@@ -1,42 +1,26 @@
-
-
+// load all dependencies
+require __DIR__ . DS . 'libreto' . DS . 'vendor' . DS . 'load.php';
-
-
-
-
+// load all helpers functions
+require __DIR__ . DS . 'libreto' . DS . 'helpers.php';
- Libreto
+// load all core classes
+function loadClass($classe) {
+ require __DIR__ . DS . 'libreto' . DS . $classe . '.php';
+}
+spl_autoload_register('loadClass');
-
-
+// load options
+require __DIR__ . DS . 'config.php';
-
-
- setBreaksEnabled(true)->text($markdown);
- echo $html;
- ?>
-
-
-
-
-
+// create Libreto
+$libreto = new Libreto($options);
-
+// launch
+$libreto->launch();
+
+// var_dump($libreto);
diff --git a/assets/ajax.php b/libreto/assets/ajax.php
similarity index 100%
rename from assets/ajax.php
rename to libreto/assets/ajax.php
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Bold.eot b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Bold.eot
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Bold.eot
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Bold.eot
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Bold.svg b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Bold.svg
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Bold.svg
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Bold.svg
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Bold.ttf b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Bold.ttf
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Bold.ttf
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Bold.ttf
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Bold.woff b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Bold.woff
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Bold.woff
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Bold.woff
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Bold.woff2 b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Bold.woff2
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Bold.woff2
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Bold.woff2
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.eot b/libreto/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.eot
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.eot
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.eot
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.svg b/libreto/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.svg
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.svg
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.svg
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.ttf b/libreto/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.ttf
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.ttf
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.ttf
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.woff b/libreto/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.woff
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.woff
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.woff
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.woff2 b/libreto/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.woff2
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.woff2
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-BoldItalic.woff2
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Italic.eot b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Italic.eot
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Italic.eot
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Italic.eot
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Italic.svg b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Italic.svg
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Italic.svg
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Italic.svg
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Italic.ttf b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Italic.ttf
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Italic.ttf
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Italic.ttf
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Italic.woff b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Italic.woff
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Italic.woff
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Italic.woff
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Italic.woff2 b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Italic.woff2
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Italic.woff2
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Italic.woff2
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Light.eot b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Light.eot
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Light.eot
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Light.eot
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Light.svg b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Light.svg
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Light.svg
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Light.svg
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Light.ttf b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Light.ttf
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Light.ttf
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Light.ttf
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Light.woff b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Light.woff
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Light.woff
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Light.woff
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Light.woff2 b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Light.woff2
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Light.woff2
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Light.woff2
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.eot b/libreto/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.eot
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-LightItalic.eot
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.eot
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.svg b/libreto/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.svg
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-LightItalic.svg
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.svg
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.ttf b/libreto/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.ttf
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-LightItalic.ttf
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.ttf
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.woff b/libreto/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.woff
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-LightItalic.woff
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.woff
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.woff2 b/libreto/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.woff2
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-LightItalic.woff2
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-LightItalic.woff2
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Medium.eot b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Medium.eot
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Medium.eot
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Medium.eot
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Medium.svg b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Medium.svg
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Medium.svg
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Medium.svg
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Medium.ttf b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Medium.ttf
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Medium.ttf
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Medium.ttf
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Medium.woff b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Medium.woff
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Medium.woff
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Medium.woff
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Medium.woff2 b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Medium.woff2
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Medium.woff2
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Medium.woff2
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.eot b/libreto/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.eot
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.eot
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.eot
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.svg b/libreto/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.svg
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.svg
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.svg
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.ttf b/libreto/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.ttf
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.ttf
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.ttf
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.woff b/libreto/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.woff
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.woff
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.woff
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.woff2 b/libreto/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.woff2
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.woff2
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-MediumItalic.woff2
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Regular.eot b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Regular.eot
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Regular.eot
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Regular.eot
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Regular.svg b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Regular.svg
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Regular.svg
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Regular.svg
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Regular.ttf b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Regular.ttf
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Regular.ttf
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Regular.ttf
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Regular.woff b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Regular.woff
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Regular.woff
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Regular.woff
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-Regular.woff2 b/libreto/assets/fonts/hkgrotesk/HKGrotesk-Regular.woff2
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-Regular.woff2
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-Regular.woff2
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.eot b/libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.eot
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-SemiBold.eot
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.eot
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.svg b/libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.svg
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-SemiBold.svg
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.svg
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.ttf b/libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.ttf
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-SemiBold.ttf
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.ttf
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.woff b/libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.woff
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-SemiBold.woff
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.woff
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.woff2 b/libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.woff2
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-SemiBold.woff2
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBold.woff2
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.eot b/libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.eot
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.eot
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.eot
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.svg b/libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.svg
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.svg
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.svg
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.ttf b/libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.ttf
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.ttf
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.ttf
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.woff b/libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.woff
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.woff
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.woff
diff --git a/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.woff2 b/libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.woff2
similarity index 100%
rename from assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.woff2
rename to libreto/assets/fonts/hkgrotesk/HKGrotesk-SemiBoldItalic.woff2
diff --git a/assets/fonts/jeanluc/jeanlucweb-bold.eot b/libreto/assets/fonts/jeanluc/jeanlucweb-bold.eot
similarity index 100%
rename from assets/fonts/jeanluc/jeanlucweb-bold.eot
rename to libreto/assets/fonts/jeanluc/jeanlucweb-bold.eot
diff --git a/assets/fonts/jeanluc/jeanlucweb-bold.otf b/libreto/assets/fonts/jeanluc/jeanlucweb-bold.otf
similarity index 100%
rename from assets/fonts/jeanluc/jeanlucweb-bold.otf
rename to libreto/assets/fonts/jeanluc/jeanlucweb-bold.otf
diff --git a/assets/fonts/jeanluc/jeanlucweb-bold.svg b/libreto/assets/fonts/jeanluc/jeanlucweb-bold.svg
similarity index 100%
rename from assets/fonts/jeanluc/jeanlucweb-bold.svg
rename to libreto/assets/fonts/jeanluc/jeanlucweb-bold.svg
diff --git a/assets/fonts/jeanluc/jeanlucweb-bold.woff b/libreto/assets/fonts/jeanluc/jeanlucweb-bold.woff
similarity index 100%
rename from assets/fonts/jeanluc/jeanlucweb-bold.woff
rename to libreto/assets/fonts/jeanluc/jeanlucweb-bold.woff
diff --git a/assets/fonts/jeanluc/jeanlucweb-thin.eot b/libreto/assets/fonts/jeanluc/jeanlucweb-thin.eot
similarity index 100%
rename from assets/fonts/jeanluc/jeanlucweb-thin.eot
rename to libreto/assets/fonts/jeanluc/jeanlucweb-thin.eot
diff --git a/assets/fonts/jeanluc/jeanlucweb-thin.otf b/libreto/assets/fonts/jeanluc/jeanlucweb-thin.otf
similarity index 100%
rename from assets/fonts/jeanluc/jeanlucweb-thin.otf
rename to libreto/assets/fonts/jeanluc/jeanlucweb-thin.otf
diff --git a/assets/fonts/jeanluc/jeanlucweb-thin.svg b/libreto/assets/fonts/jeanluc/jeanlucweb-thin.svg
similarity index 100%
rename from assets/fonts/jeanluc/jeanlucweb-thin.svg
rename to libreto/assets/fonts/jeanluc/jeanlucweb-thin.svg
diff --git a/assets/fonts/jeanluc/jeanlucweb-thin.woff b/libreto/assets/fonts/jeanluc/jeanlucweb-thin.woff
similarity index 100%
rename from assets/fonts/jeanluc/jeanlucweb-thin.woff
rename to libreto/assets/fonts/jeanluc/jeanlucweb-thin.woff
diff --git a/assets/fonts/jeanluc/stylesheet.css b/libreto/assets/fonts/jeanluc/stylesheet.css
similarity index 100%
rename from assets/fonts/jeanluc/stylesheet.css
rename to libreto/assets/fonts/jeanluc/stylesheet.css
diff --git a/assets/fonts/spungold/spungold.eot b/libreto/assets/fonts/spungold/spungold.eot
similarity index 100%
rename from assets/fonts/spungold/spungold.eot
rename to libreto/assets/fonts/spungold/spungold.eot
diff --git a/assets/fonts/spungold/spungold.svg b/libreto/assets/fonts/spungold/spungold.svg
similarity index 100%
rename from assets/fonts/spungold/spungold.svg
rename to libreto/assets/fonts/spungold/spungold.svg
diff --git a/assets/fonts/spungold/spungold.ttf b/libreto/assets/fonts/spungold/spungold.ttf
similarity index 100%
rename from assets/fonts/spungold/spungold.ttf
rename to libreto/assets/fonts/spungold/spungold.ttf
diff --git a/assets/fonts/spungold/spungold.woff b/libreto/assets/fonts/spungold/spungold.woff
similarity index 100%
rename from assets/fonts/spungold/spungold.woff
rename to libreto/assets/fonts/spungold/spungold.woff
diff --git a/assets/images/carnet-dessine.png b/libreto/assets/images/carnet-dessine.png
similarity index 100%
rename from assets/images/carnet-dessine.png
rename to libreto/assets/images/carnet-dessine.png
diff --git a/assets/images/libretonet.png b/libreto/assets/images/libretonet.png
similarity index 100%
rename from assets/images/libretonet.png
rename to libreto/assets/images/libretonet.png
diff --git a/libreto/assets/images/logo.png b/libreto/assets/images/logo.png
new file mode 100644
index 0000000..4abedfc
Binary files /dev/null and b/libreto/assets/images/logo.png differ
diff --git a/assets/images/logo.svg b/libreto/assets/images/logo.svg
similarity index 100%
rename from assets/images/logo.svg
rename to libreto/assets/images/logo.svg
diff --git a/libreto/assets/js/bindery-controls.min.js b/libreto/assets/js/bindery-controls.min.js
new file mode 100644
index 0000000..9431a1f
--- /dev/null
+++ b/libreto/assets/js/bindery-controls.min.js
@@ -0,0 +1,2 @@
+var BinderyControls=function(){"use strict";var e,t,n,o=(t=String.prototype.split,n=/()??/.exec("")[1]===e,function(o,r,i){if("[object RegExp]"!==Object.prototype.toString.call(r))return t.call(o,r,i);var a,s,l,p,c=[],u=(r.ignoreCase?"i":"")+(r.multiline?"m":"")+(r.extended?"x":"")+(r.sticky?"y":""),d=0;for(r=new RegExp(r.source,u+"g"),o+="",n||(a=new RegExp("^"+r.source+"$(?!\\s)",u)),i=i===e?-1>>>0:i>>>0;(s=r.exec(o))&&!((l=s.index+s[0].length)>d&&(c.push(o.slice(d,s.index)),!n&&s.length>1&&s[0].replace(a,function(){for(var t=1;t1&&s.index=i));)r.lastIndex===s.index&&r.lastIndex++;return d===o.length?!p&&r.test("")||c.push(""):c.push(o.slice(d)),c.length>i?c.slice(0,i):c}),r=[].indexOf,i=function(e,t){if(r)return e.indexOf(t);for(var n=0;n-1||(t.push(e),p(t))}function r(e){var t=l(),n=i(t,e);-1!==n&&(t.splice(n,1),p(t))}function a(e){return i(l(),e)>-1}function l(){var t=e.className;return function(e,t){for(var n=[],o=0;o /g,">")}function y(e){return g(e).replace(/"/g,""")}function v(){}function w(){}function b(e){e.__defineGetter__("parentNode",function(){return this.parentElement})}u.prototype.createTextNode=function(e){var t=new v;return t.textContent=e,t.nodeName="#text",t.nodeType=3,t},u.prototype.createElement=function(e){var t=new m;return t.nodeName=t.tagName=e,t},u.prototype.createComment=function(e){var t=new w;return t.data=e,t},v.prototype=new d,m.prototype=new d,w.prototype=new d,f.prototype.setProperty=function(e,t){this.el._setProperty(this.styles,{name:e,value:t})},f.prototype.getProperty=function(e){return this.el._getProperty(this.styles,e)},f.prototype.__defineGetter__("cssText",function(){var e="";return this.styles.forEach(function(t){e+=t.name+":"+t.value+";"}),e}),f.prototype.__defineSetter__("cssText",function(e){this.styles.length=0,e.split(";").forEach(function(e){var t=e.indexOf(":");if(t){var n=e.slice(0,t).trim(),o=e.slice(t+1).trim();this.setProperty(n,o)}},this)}),m.prototype.nodeType=1,m.prototype.appendChild=function(e){return e.parentElement=this,this.childNodes.push(e),e},m.prototype.setAttribute=function(e,t){"style"==e?this.style.cssText=t:this._setProperty(this.attributes,h,e,t)},m.prototype.getAttribute=function(e){if("style"==e)return this.style.cssText;var t=this._getProperty(this.attributes,e);return void 0!==t?t.value:null},m.prototype.removeAttribute=function(e){if("class"===e)delete this.className;else for(var t=0,n=this.attributes.length;t"),{AREA:!0,BASE:!0,BR:!0,COL:!0,EMBED:!0,HR:!0,IMG:!0,INPUT:!0,KEYGEN:!0,LINK:!0,META:!0,PARAM:!0,SOURCE:!0,TRACK:!0,WBR:!0}[this.nodeName.toUpperCase()]||(e.push(this.innerHTML),e.push(""+this.nodeName+">")),e.join("")}),m.prototype.__defineGetter__("textContent",function(){var e="";return this.childNodes.forEach(function(t){e+=t.textContent}),e}),m.prototype.__defineSetter__("textContent",function(e){var t=new v;return t.textContent=e,this.childNodes=[t],e}),m.prototype.addEventListener=function(e,t){},m.prototype.nodeValue=null,v.prototype.nodeType=3,v.prototype.nodeName="#text",v.prototype.__defineGetter__("textContent",function(){return g(this.value||"")}),v.prototype.__defineSetter__("textContent",function(e){this.value=e}),v.prototype.__defineGetter__("nodeValue",function(){return g(this.value||"")}),v.prototype.__defineSetter__("nodeValue",function(e){this.value=e}),v.prototype.__defineGetter__("length",function(){return(this.value||"").length}),v.prototype.replaceData=function(e,t,n){this.value=this.value.slice(0,e)+n+this.value.slice(e+t)},w.prototype.nodeType=8,w.prototype.nodeName="#comment",w.prototype.__defineGetter__("data",function(){return this.value}),w.prototype.__defineSetter__("data",function(e){this.value=e}),w.prototype.__defineGetter__("outerHTML",function(){return"\x3c!--"+g(this.value||"")+"--\x3e"}),w.prototype.__defineGetter__("nodeValue",function(){return g(this.value||"")}),w.prototype.__defineSetter__("nodeValue",function(e){this.value=e}),b(m.prototype),b(w.prototype),b(v.prototype),b(d.prototype);var x,S={Document:u,Node:d,Element:m,Comment:w,Text:v,document:new u},_=(function(e){var t="undefined"==typeof window?S:window,n=t.document,r=t.Text;function i(){var e=[];function t(){var t=[].slice.call(arguments),i=null;function p(t){var c,u,d;if(null==t);else if("string"==typeof t)i?i.appendChild(c=n.createTextNode(t)):(d=o(t,/([\.#]?[^\s#.]+)/),/^\.|#/.test(d[1])&&(i=n.createElement("div")),l(d,function(e){var t=e.substring(1,e.length);e&&(i?"."===e[0]?a(i).add(t):"#"===e[0]&&i.setAttribute("id",t):i=n.createElement(e))}));else if("number"==typeof t||"boolean"==typeof t||t instanceof Date||t instanceof RegExp)i.appendChild(c=n.createTextNode(t.toString()));else if(u=t,"[object Array]"==Object.prototype.toString.call(u))l(t,p);else if(s(t))i.appendChild(c=t);else if(t instanceof r)i.appendChild(c=t);else if("object"==typeof t)for(var f in t)if("function"==typeof t[f])/^on\w+/.test(f)?function(t,n){i.addEventListener?(i.addEventListener(t.substring(2),n[t],!1),e.push(function(){i.removeEventListener(t.substring(2),n[t],!1)})):(i.attachEvent(t,n[t]),e.push(function(){i.detachEvent(t,n[t])}))}(f,t):(i[f]=t[f](),e.push(t[f](function(e){i[f]=e})));else if("style"===f)if("string"==typeof t[f])i.style.cssText=t[f];else for(var h in t[f])!function(n,o){if("function"==typeof o)i.style.setProperty(n,o()),e.push(o(function(e){i.style.setProperty(n,e)}));else var r=t[f][n].match(/(.*)\W+!important\W*$/);r?i.style.setProperty(n,r[1],"important"):i.style.setProperty(n,t[f][n])}(h,t[f][h]);else if("attrs"===f)for(var m in t[f])i.setAttribute(m,t[f][m]);else"data-"===f.substr(0,5)?i.setAttribute(f,t[f]):i[f]=t[f];else if("function"==typeof t){m=t();i.appendChild(c=s(m)?m:n.createTextNode(m)),e.push(t(function(e){s(e)&&c.parentElement?(c.parentElement.replaceChild(e,c),c=e):c.textContent=e}))}return c}for(;t.length;)p(t.shift());return i}return t.cleanup=function(){for(var t=0;t100&&(G=t,w.textContent=e+" Pages")},this.setDone=function(e){w.textContent=e+" Pages"},this.setInvalid=function(){},u.classList.add(E("btn-print"));var R=A(g,v,y);R.classList.add(E("print-options")),l=T.apply(void 0,[{onchange:function(e){return o.setMode(e.target.value)}}].concat(N([C({value:r.PREVIEW},"Preview"),C({value:r.FLIPBOOK},"Flipbook"),C({value:r.PRINT},"Print Preview")].map(function(e){return e.value===n.mode&&(e.selected=!0),e}))));var M=A(l);M.classList.add(E("view-row")),this.element=_(E(".controls"),B,O,M,R,u)}}();
+//# sourceMappingURL=bindery-controls.min.js.map
diff --git a/libreto/assets/js/bindery.min.js b/libreto/assets/js/bindery.min.js
new file mode 100644
index 0000000..c15c18a
--- /dev/null
+++ b/libreto/assets/js/bindery.min.js
@@ -0,0 +1,2 @@
+var Bindery=function(){"use strict";var e="v2.1.0",t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},r=function(){function e(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:[],n=document.createElement("div");return n.className=e.split(".").filter(function(e){return""!==e}).map(k).join(" "),"string"==typeof t?n.textContent=t:Array.isArray(t)&&t.forEach(function(e){return n.appendChild(e)}),n},P=function(){function e(){n(this,e),this.flowContent=C("content"),this.flowBox=C("flowbox",[this.flowContent]),this.footer=C("footer"),this.background=C("background"),this.element=C("page",[this.background,this.flowBox,this.footer])}return r(e,[{key:"overflowAmount",value:function(){var e=this.flowContent.offsetHeight,t=this.flowBox.offsetHeight;if(0===t)throw Error("Bindery: Trying to flow into a box of zero height.");return e-t}},{key:"hasOverflowed",value:function(){return this.overflowAmount()>-5}},{key:"setLeftRight",value:function(e){if("left"===e)this.side=e,this.element.classList.remove(k("right")),this.element.classList.add(k("left"));else{if("right"!==e)throw Error("Bindery: Setting page to invalid direction"+e);this.side=e,this.element.classList.remove(k("left")),this.element.classList.add(k("right"))}}},{key:"setPreference",value:function(e){"left"===e&&(this.alwaysLeft=!0),"right"===e&&(this.alwaysRight=!0)}},{key:"suppressErrors",get:function(){return this.suppress||!1},set:function(e){this.suppress=e,e?this.element.classList.add(k("is-overflowing")):this.element.classList.remove(k("is-overflowing"))}},{key:"isEmpty",get:function(){return!this.hasOutOfFlowContent&&""===this.flowContent.textContent.trim()&&this.flowContent.offsetHeight<1}},{key:"isLeft",get:function(){return"left"===this.side}},{key:"isRight",get:function(){return"right"===this.side}}],[{key:"isSizeValid",value:function(){document.body.classList.remove(k("viewing"));var t=new e,n=document.querySelector(k(".measure-area"));n||(n=document.body.appendChild(C("measure-area"))),n.innerHTML="",n.appendChild(t.element);var r=t.flowBox.getBoundingClientRect();return n.parentNode.removeChild(n),r.height>100&&r.width>100}}]),e}(),S=function e(t){return!!t.hasAttribute("data-ignore-overflow")||!!t.parentElement&&e(t.parentElement)},x=new(function(){function e(){n(this,e),this.numberOfCalls=0,this.resumeLimit=1/0,this.callsSinceResume=0,this.queuedFunc=null,this.isPaused=!1,this.useDelay=!1,this.delayTime=100,this.lastWaitedTime=0}return r(e,[{key:"throttle",value:function(e){var t=this;this.callsSinceResume+=1,this.callsSinceResume>this.resumeLimit&&this.endResume();var n=performance.now()-this.lastWaitedTime;this.isPaused?this.queuedFunc=e:this.useDelay?setTimeout(e,this.delayTime):this.numberOfCalls<800&&n<50?(this.numberOfCalls+=1,e()):(this.numberOfCalls=0,document.hidden?setTimeout(e,1):requestAnimationFrame(function(n){t.lastWaitedTime=n,e()}))}},{key:"pause",value:function(){return this.isPaused?"Already paused":(this.isPaused=!0,"Paused")}},{key:"resumeDelay",value:function(){this.useDelay=!0,this.resume()}},{key:"finish",value:function(){this.useDelay=!1,this.resume()}},{key:"resume",value:function(){return this.isPaused?(this.isPaused=!1,this.queuedFunc?(this.queuedFunc(),this.queuedFunc=null,"Resuming"):"Layout complete"):"Already running"}},{key:"step",value:function(){if(!this.isPaused)return this.pause();if(this.queuedFunc){var e=this.queuedFunc,t=e.name;return this.queuedFunc=null,e(),t}return"Layout complete"}},{key:"resumeFor",value:function(e){return this.callsSinceResume=0,this.resumeLimit=e,this.resume()}},{key:"endResume",value:function(){console.log("Paused after "+this.resumeLimit),this.resumeLimit=1/0,this.callsSinceResume=0,this.pause()}},{key:"isDebugging",get:function(){return this.useDelay},set:function(e){this.useDelay=e,e&&(window.binderyDebug={pause:this.pause.bind(this),resume:this.resume.bind(this),resumeFor:this.resumeFor.bind(this),step:this.step.bind(this),finish:this.finish.bind(this)},console.log("Bindery: Debug layout with the following: \nbinderyDebug.pause() \nbinderyDebug.resume()\n binderyDebug.resumeFor(n) // pauses after n steps, \nbinderyDebug.step()"))}}]),e}()),E=function(){function e(t){n(this,e),this.successArgs=[],this.errorArgs=[],this.successCallback=null,this.errorCallback=null,this.progressCallback=null,this.chainedThenable=null,this.chainedSuccessCallback=null,this.chainedErrorCallback=null,this.isRejected=!1,this.isResolved=!1,this.resolve=this.resolve.bind(this),this.reject=this.reject.bind(this),this.updateProgress=this.updateProgress.bind(this),t&&t(this.resolve,this.reject,this.updateProgress)}return r(e,[{key:"then",value:function(e){if(this.chainedThenable)return this.chainedThenable.then(e);if(!this.successCallback)return this.successCallback=e,this.isResolved&&this.resolve(),this;if(!this.chainedSuccessCallback)return this.chainedSuccessCallback=e,this.isResolved&&this.resolveChained(),this;throw Error("need real chained then")}},{key:"progress",value:function(e){return this.progressCallback=e,this}},{key:"catch",value:function(e){if(this.chainedThenable)return this.chainedThenable.catch(e);if(!this.errorCallback)return this.errorCallback=e,this.isRejected&&this.reject(),this;if(!this.chainedErrorCallback)return this.chainedErrorCallback=e,this.isRejected&&this.rejectChained(),this;throw Error("need real chained catch")}},{key:"resolve",value:function(){this.isResolved=!0;for(var e=arguments.length,t=Array(e),n=0;n0&&(this.successArgs=t),null!==this.successCallback){var r=void 0;(r=this.successArgs.length>0?this.successCallback.apply(this,a(this.successArgs)):this.successCallback())&&r.then&&(this.chainedThenable=r),this.chainedSuccessCallback&&this.resolveChained()}}},{key:"resolveChained",value:function(){this.chainedThenable?this.chainedThenable.then(this.chainedSuccessCallback):this.chainedSuccessCallback()}},{key:"reject",value:function(){this.isRejected=!0;for(var e=arguments.length,t=Array(e),n=0;n0&&(this.errorArgs=t),null!==this.errorCallback){var r=void 0;(r=this.errorArgs.length>0?this.errorCallback.apply(this,a(this.errorArgs)):this.errorCallback())&&r.then&&(this.chainedThenable=r),this.successCallback&&r&&r.then&&r.then(this.successCallback),this.chainedSuccessCallback&&(r&&r.then?r.then(this.chainedSuccessCallback):this.chainedSuccessCallback()),this.chainedErrorCallback&&this.rejectChained()}}},{key:"rejectChained",value:function(){this.chainedThenable?this.chainedThenable.catch(this.chainedErrorCallback):this.chainedErrorCallback()}},{key:"updateProgress",value:function(){null!==this.progressCallback&&this.progressCallback.apply(this,arguments)}}],[{key:"resolved",value:function(){return new e(function(e){return e()})}}]),e}(),O=function(e,t,n){return new E(function(r,i){t.appendChild(e),n.hasOverflowed()?(t.removeChild(e),x.throttle(i)):x.throttle(r)})},T=function(e,t,n){return new E(function(r,i){var o=e.nodeValue;if(t.appendChild(e),n.hasOverflowed()&&!S(t)){var s=0;!function t(){if(e.nodeValue=o.substr(0,s),n.hasOverflowed()){for(" "===o.charAt(s)&&(s-=1);" "!==o.charAt(s)&&s>0;)s-=1;if(s<1)return e.nodeValue=o,e.parentNode.removeChild(e),void x.throttle(i);var a=o.substr(0,s),l=o.substr(s);e.nodeValue=a;var u=document.createTextNode(l);x.throttle(function(){return r(u)})}else if(s>o.length-1)x.throttle(r);else{for(s+=1;" "!==o.charAt(s)&&s0&&(r="."+[].concat(a(e.classList)).join("."));var i="";return n.length<1&&r.length<2&&(i='("'+e.textContent.substr(0,30).replace(/\s+/g," ")+'...")'),t+n+r+i},I=function(e){return e instanceof R||e instanceof L||e instanceof B},N=function(e){var t=e.filter(I),n=e.filter(function(e){return!t.includes(e)}),r=t.find(function(e){return e instanceof R}),i=t.find(function(e){return e instanceof L});return r?n.push(r):i?n.push(i):n.push.apply(n,a(t)),n},F=function(){function e(t){n(this,e),this.rules=t,this.pageRules=t.filter(function(e){return e.eachPage}),this.beforeAddRules=t.filter(function(e){return e.selector&&e.beforeAdd}),this.afterAddRules=t.filter(function(e){return e.selector&&e.afterAdd}),this.selectorsNotToSplit=t.filter(function(e){return e.avoidSplit}).map(function(e){return e.selector})}return r(e,[{key:"setup",value:function(){this.rules.forEach(function(e){e.setup&&e.setup()})}},{key:"startPage",value:function(e,t){this.rules.forEach(function(n){n.afterPageCreated&&n.afterPageCreated(e,t)})}},{key:"finishEveryPage",value:function(e){this.pageRules.forEach(function(t){e.pages.forEach(function(n){t.eachPage(n,e)})})}},{key:"finishPage",value:function(e,t){this.pageRules.forEach(function(n){n.eachPage(e,t)})}},{key:"beforeAddElement",value:function(e,t,n,r){var i=e;return this.beforeAddRules.filter(function(e){return i.matches(e.selector)}).forEach(function(e){i=e.beforeAdd(i,t,n,r)}),i}},{key:"afterAddElement",value:function(e,t,n,r,i){var o=e,s=this.afterAddRules.filter(function(e){return o.matches(e.selector)});return N(s).forEach(function(e){o=e.afterAdd(o,t,n,r,function(o){return i(o),e.afterAdd(o,t,n,r,function(){console.log("Couldn't apply "+e.name+" to "+_(o)+". Caused overflows twice.")})})}),o}}]),e}(),M=function(e,t){for(var n=t;nr&&(t[e]="")})),""!==t[n]&&(e.heading[n]=t[n])})})},H=function(e,t){for(var n=[],r=t.filter(function(e){return e.customToNextClass}).map(function(e){return e.customToNextClass}),i=t.filter(function(e){return e.customFromPreviousClass}).map(function(e){return e.customFromPreviousClass}),o=e.length-1;o>=0;o-=1){var s=e[o],a=s.cloneNode(!1);if(a.innerHTML="",function(e){e.classList.add(k("continues")),r.forEach(function(t){return e.classList.add(t)})}(s),function(e){e.classList.add(k("continuation")),i.forEach(function(t){return e.classList.add(t)})}(a),"OL"===a.tagName){var l=1;s.hasAttribute("start")&&(l=parseInt(s.getAttribute("start"),10)),o0&&void 0!==arguments[0]&&arguments[0];if(f.pages.length>2e3)throw r("Maximum page count exceeded"),Error("Bindery: Maximum page count exceeded. Suspected runaway layout.");b(f.pageInProgress,e),p=H(p,t);var n=m();if(f.pageInProgress=n,i(f),f.pages.push(n),p[0]&&n.flowContent.appendChild(p[0]),n.hasOverflowed()){var o=u(p);o?(console.warn("Bindery: Content overflows, probably due to a style set on "+_(o)+"."),o.parentNode.removeChild(o)):console.warn("Bindery: Content overflows.")}return n},y=function(e){if(p.pop(),p.length<1)throw Error("Bindery: Attempting to move the top-level element");for(var t=e,n=[];p.length>1&&!J(u(p),h.selectorsNotToSplit);)t=p.pop(),n.unshift(t);t.setAttribute("data-bindery-did-move",!0);var r=t.parentNode;r.removeChild(t),p.length>1&&""===u(p).textContent.trim()&&(r.appendChild(t),t=p.pop(),n.unshift(t),t.parentNode.removeChild(t)),f.pageInProgress.isEmpty||(f.pageInProgress.hasOverflowed()&&(f.pageInProgress.suppressErrors=!0),v()),u(p).appendChild(t),n.forEach(function(e){p.push(e)}),p.push(e)},w=function(e,t){return new E(function(n){t.appendChild(e),g()&&(f.pageInProgress.suppressErrors=!0,v()),n()})},k=function e(t){return new E(function(n,r){T(t,u(p),f.pageInProgress).then(function(t){t?(v(),e(t).then(n).catch(r)):n()}).catch(r)})},x=function(e){return J(e,h.selectorsNotToSplit)&&!S(e)},A=function(e,t){return(x(t)?k(e).catch(function(){return p.length<2?w(e,u(p)):(y(t),k(e))}):O(e,u(p),f.pageInProgress).catch(function(){return g()&&y(t),O(e,u(p),f.pageInProgress)})).catch(function(){return w(e,u(p))})},z=void 0,L=function(e,t){if(U(e))return A(e,t);if(K(e)){var n=performance.now();return q(e).then(function(){return o+=performance.now()-n,z(e)})}return Y(e)?z(e):E.resolved()};z=function(e){return new E(function(t){f.pageInProgress.hasOverflowed()&&g()&&(f.pageInProgress.suppressErrors=!0,v());var n=h.beforeAddElement(e,f,v,m);p[0]?u(p).appendChild(n):f.pageInProgress.flowContent.appendChild(n),p.push(n);var r=[].concat(a(n.childNodes));n.innerHTML="",f.pageInProgress.hasOverflowed()&&g()&&y(n);var i=function(){var e=p.pop();h.afterAddElement(e,f,v,m,function(e){e.parentNode.removeChild(e),v(),u(p).appendChild(e)}),c+=1,f.estimatedProgress=c/s,t()},o=0;!function e(){if(o0&&void 0!==arguments[0]?arguments[0]:{};n(this,e),this.setSize(t.size||ie.size),this.setMargin(t.margin||ie.margin),this.setBleed(t.bleed||ie.bleed)}return r(e,[{key:"setupPaper",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.sheetSizeMode=oe?e.paper||Q.AUTO:Q.AUTO_MARKS,this.printTwoUp=e.layout&&e.layout!==ee.PAGES}},{key:"setSize",value:function(e){p(e),this.size=e}},{key:"setMargin",value:function(e){p(e),this.margin=e}},{key:"setBleed",value:function(e){this.bleed=e}},{key:"setPrintTwoUp",value:function(e){this.printTwoUp=e}},{key:"isSizeValid",value:function(){return this.updateStylesheet(),P.isSizeValid()}},{key:"spreadSizeStyle",value:function(){var e=f(this.size.width);return{height:this.size.height,width:""+2*e.val+e.unit}}},{key:"updateStylesheet",value:function(){var e=void 0,t=document.querySelector("#binderyPageSetup");t?e=t:(e=document.createElement("style")).id="binderyPageSetup";var n=f(this.size.width);e.innerHTML="\n@page { size: "+this.sheetSize.width+" "+this.sheetSize.height+"; }\n"+k(".print-page")+" { width: "+this.sheetSize.width+"; height: "+this.sheetSize.height+";}\n\n"+k(".show-crop")+" "+k(".print-page")+" "+k(".spread-wrapper")+",\n"+k(".show-bleed-marks")+" "+k(".print-page")+" "+k(".spread-wrapper")+" {\n margin: calc("+this.bleed+" + 12pt) auto;\n}\nhtml {\n --bindery-page-width: "+this.size.width+";\n --bindery-page-height: "+this.size.height+";\n}\n"+k(".page-size-rotated")+" {\n height: "+this.size.width+";\n width: "+this.size.height+";\n}\n"+k(".spread-size")+" {\n height: "+this.size.height+";\n width: "+2*n.val+n.unit+";\n}\n"+k(".spread-size-rotated")+" {\n height: "+2*n.val+n.unit+";\n width: "+this.size.height+";\n}\n"+k(".flowbox")+",\n"+k(".footer")+" {\n margin-left: "+this.margin.inner+";\n margin-right: "+this.margin.outer+";\n}\n"+k(".left")+" "+k(".flowbox")+",\n"+k(".left")+" "+k(".footer")+" {\n margin-left: "+this.margin.outer+";\n margin-right: "+this.margin.inner+";\n}\n\n"+k(".left")+" "+k(".running-header")+" {\n left: "+this.margin.outer+";\n}\n"+k(".right")+" "+k(".running-header")+" {\n right: "+this.margin.outer+";\n}\n\n"+k(".flowbox")+" { margin-top: "+this.margin.top+"; }\n"+k(".footer")+"{ margin-bottom: "+this.margin.bottom+"; }\n\n"+k(".bleed-left")+",\n"+k(".bleed-right")+",\n"+k(".crop-left")+",\n"+k(".crop-right")+",\n"+k(".crop-fold")+" {\n top: calc( -12pt - "+this.bleed+" );\n bottom: calc( -12pt - "+this.bleed+" );\n}\n\n"+k(".bleed-top")+",\n"+k(".bleed-bottom")+",\n"+k(".crop-top")+",\n"+k(".crop-bottom")+" {\n left: calc( -12pt - "+this.bleed+" );\n right: calc( -12pt - "+this.bleed+" );\n}\n"+k(".bleed-left")+" { left: -"+this.bleed+"; }\n"+k(".bleed-right")+" { right: -"+this.bleed+"; }\n"+k(".bleed-top")+" { top: -"+this.bleed+"; }\n"+k(".bleed-bottom")+" { bottom: -"+this.bleed+"; }\n\n"+k(".background")+" {\n top: -"+this.bleed+";\n bottom: -"+this.bleed+";\n left: -"+this.bleed+";\n right: -"+this.bleed+";\n}\n\n"+k(".spread")+k(".right")+" > "+k(".background")+" {\n left: calc(-100% - "+this.bleed+");\n}\n"+k(".spread")+k(".left")+" > "+k(".background")+" {\n right: calc(-100% - "+this.bleed+");\n}\n ",document.head.appendChild(e)}},{key:"displaySize",get:function(){return{width:this.printTwoUp?this.spreadSizeStyle().width:this.size.width,height:this.size.height,bleed:this.bleed}}},{key:"sheetSize",get:function(){var e=this.printTwoUp?this.spreadSizeStyle().width:this.size.width,t=this.size.height,n=this.bleed;switch(this.sheetSizeMode){case Q.AUTO:return{width:e,height:t};case Q.AUTO_BLEED:return{width:"calc("+e+" + "+n+" + "+n+")",height:"calc("+t+" + "+n+" + "+n+")"};case Q.AUTO_MARKS:return{width:"calc("+e+" + "+n+" + "+n+" + 24pt)",height:"calc("+t+" + "+n+" + "+n+" + 24pt)"};case Q.LETTER_LANDSCAPE:return{width:ne.height,height:ne.width};case Q.LETTER_PORTRAIT:return ne;case Q.A4_PORTRAIT:return re;case Q.A4_LANDSCAPE:return{width:re.height,height:re.width}}return{width:e,height:t}}}]),e}(),ae=function(t,n){return C(".error",[C(".error-title",t),C(".error-text",n),C(".error-footer","Bindery "+e)])},le=function(e,t){for(;e.length%4!=0;){var n=t();n.element.style.visibility="hidden",e.push(n)}for(var r=[],i=e.length,o=0;o60&&(a=60/e.length),i.style.width=e.length*a+"px";for(var l=function(e){var t=e;t===s&&(t+=1),s=t;var n=4;o.length*n>200&&(n=200/o.length),o.forEach(function(e,r,i){var o=(i.length-Math.abs(r-t+.5))*n;e.style.transform="translate3d("+(rDefault Replacement'),e}}]),t}(),Te=function(e){function t(e){n(this,t);var r=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return b.validate(e,{name:"Footnote",selector:b.string,replace:b.func,render:b.func}),r}return o(t,Oe),r(t,[{key:"afterAdd",value:function(e,n,r,o,s){var a=n.pageInProgress.footer.children.length+1,l=C(".footnote"),u=this.render(e,a);return u instanceof HTMLElement?l.appendChild(u):l.innerHTML=u,n.pageInProgress.footer.appendChild(l),i(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"afterAdd",this).call(this,e,n,r,o,function(e){return n.pageInProgress.footer.removeChild(l),s(e)})}},{key:"createReplacement",value:function(e,t){var n=e.pageInProgress.footer.children.length;return this.replace(t,n)}},{key:"replace",value:function(e,t){return e.insertAdjacentHTML("beforeEnd",''+t+" "),e}},{key:"render",value:function(e,t){return""+t+" Default footnote (Learn how to change it )"}}]),t}(),Ae=function(e){function t(e){n(this,t);var r=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return b.validate(e,{name:"PageReference",selector:b.string,replace:b.func,createTest:b.func}),r}return o(t,Oe),r(t,[{key:"afterAdd",value:function(e,t){var n=this,r=this.createTest(e);if(r){var i=e.parentNode,o=e.cloneNode(!0),s=t.pagesForTest(r),a=c(s),l=this.replace(o,a||"###");return l.classList.add(k("placeholder-pulse")),i.replaceChild(l,e),t.onComplete(function(){var i=l.parentNode,o=e.cloneNode(!0),s=t.pagesForTest(r),a=c(s),u=n.replace(o,a);i.replaceChild(u,l)}),l}return e}},{key:"createTest",value:function(e){var t=e.getAttribute("href");return t?(t=t.replace("#",""),t='[id="'+t+'"]',function(e){return e.querySelector(t)}):null}},{key:"replace",value:function(e,t){return e.insertAdjacentHTML("beforeend",", "+t+" "),e}}]),t}(),ze=function(e){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};n(this,t);var r=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return b.validate(e,{name:"RunningHeader",render:b.func}),r}return o(t,A),r(t,[{key:"eachPage",value:function(e){if(!e.runningHeader){var t=C(".running-header");e.element.appendChild(t),e.runningHeader=t}e.runningHeader.innerHTML=this.render(e)}},{key:"render",value:function(e){return e.number}}]),t}(),Le={Rule:A,Split:function(e){return new xe(e)},Counter:function(e){return new Ee(e)},FullBleedPage:function(e){return new L(e)},Footnote:function(e){return new Te(e)},RunningHeader:function(e){return new ze(e)},Replace:function(e){return new Oe(e)},FullBleedSpread:function(e){return new R(e)},PageBreak:function(e){return new B(e)},PageReference:function(e){return new Ae(e)},createRule:function(e){return new A(e)}},Re=Le.PageBreak,Be=Le.PageReference,_e=Le.Footnote,Ie=Le.FullBleedPage,Ne=Le.FullBleedSpread,Fe=function(e,t){return e.textContent=""+t,e},Me=[Re({selector:'[book-page-break="both"]',position:"both"}),Re({selector:'[book-page-break="avoid"]',position:"avoid"}),Re({selector:'[book-page-break="after"][book-page-continue="right"]',position:"after",continue:"right"}),Re({selector:'[book-page-break="after"][book-page-continue="left"]',position:"after",continue:"left"}),Re({selector:'[book-page-break="after"][book-page-continue="next"]',position:"after",continue:"next"}),Re({selector:'[book-page-break="before"][book-page-continue="right"]',position:"before",continue:"right"}),Re({selector:'[book-page-break="before"][book-page-continue="left"]',position:"before",continue:"left"}),Re({selector:'[book-page-break="before"][book-page-continue="next"]',position:"before",continue:"next"}),Ie({selector:'[book-full-bleed="page"]'}),Ne({selector:'[book-full-bleed="spread"]'}),_e({selector:"[book-footnote-text]",render:function(e,t){return""+t+" "+e.getAttribute("book-footnote-text")}}),Be({selector:"[book-pages-with-text]",replace:Fe,createTest:function(e){var t=e.getAttribute("book-pages-with-text").toLowerCase().trim();return function(e){return e.textContent.toLowerCase().includes(t)}}}),Be({selector:"[book-pages-with-selector]",replace:Fe,createTest:function(e){var t=e.getAttribute("book-pages-with-selector").trim();return function(e){return e.querySelector(t)}}}),Be({selector:"[book-pages-with]",replace:Fe,createTest:function(e){var t=e.textContent.toLowerCase().trim();return function(e){return e.textContent.toLowerCase().includes(t)}}})];!function(e){if(e&&"undefined"!=typeof window){var t=document.createElement("style");t.setAttribute("type","text/css"),t.innerHTML=e,document.head.appendChild(t)}}('@charset "UTF-8";@media screen{.📖-page{background:#fff;outline:1px solid #ddd;box-shadow:0 2px 4px -1px rgba(0,0,0,.15);overflow:hidden}.📖-show-bleed .📖-page{box-shadow:none;outline:none;overflow:visible}.📖-page:after{content:"";position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;z-index:3}}li.📖-continuation,p.📖-continuation{text-indent:unset!important}li.📖-continuation{list-style:none!important}.📖-out-of-flow{display:none}.📖-page{width:var(--bindery-page-width);height:var(--bindery-page-height);position:relative;display:flex;flex-direction:column;flex-wrap:nowrap}.📖-flowbox{position:relative;margin:60px 40px;margin-bottom:0;flex:1 1 auto;min-height:0}.📖-content{padding:.1px;position:relative}.📖-footer{margin:60px 40px;margin-top:8pt;flex:0 1 auto;z-index:1}.📖-background{position:absolute;z-index:0;overflow:hidden}.📖-left>.📖-background{right:0}.📖-right>.📖-background{left:0}.📖-sup{font-size:.667em}.📖-footer,.📖-running-header{font-size:10pt}.📖-running-header{position:absolute;text-align:center;top:.25in}.📖-left .📖-running-header{left:18pt;text-align:left}.📖-right .📖-running-header{right:18pt;text-align:right}.📖-left .📖-rotate-container.📖-rotate-outward,.📖-left .📖-rotate-container.📖-rotate-spread-clockwise,.📖-right .📖-rotate-container.📖-rotate-inward,.📖-rotate-container.📖-rotate-clockwise{transform:rotate(90deg) translate3d(0,-100%,0);transform-origin:top left}.📖-left .📖-rotate-container.📖-rotate-inward,.📖-left .📖-rotate-container.📖-rotate-spread-counterclockwise,.📖-right .📖-rotate-container.📖-rotate-outward,.📖-rotate-container.📖-rotate-counterclockwise{transform:rotate(-90deg) translate3d(-100%,0,0);transform-origin:top left}.📖-rotate-container{position:absolute}.📖-left .📖-rotate-container.📖-rotate-clockwise .📖-background{bottom:0}.📖-left .📖-rotate-container.📖-rotate-counterclockwise .📖-background,.📖-right .📖-rotate-container.📖-rotate-clockwise .📖-background{top:0}.📖-right .📖-rotate-container.📖-rotate-counterclockwise .📖-background,.📖-rotate-container.📖-rotate-inward .📖-background{bottom:0}.📖-rotate-container.📖-rotate-outward .📖-background{top:0}.📖-right .📖-rotate-container.📖-rotate-spread-clockwise{transform:rotate(90deg) translate3d(0,-50%,0);transform-origin:top left}.📖-right .📖-rotate-container.📖-rotate-spread-counterclockwise{transform:rotate(-90deg) translate3d(-100%,-50%,0);transform-origin:top left}@media screen{.📖-viewing{background:#f4f4f4!important}.📖-root{transition:opacity .2s;opacity:1;background:#f4f4f4;padding:10px;z-index:2;position:relative;padding-top:60px;min-height:90vh}.📖-progress-bar{position:fixed;left:0;top:0;background:var(--bindery-ui-accent,#0000c5);width:0;transition:all .2s;opacity:0;height:0;z-index:2}.📖-in-progress .📖-progress-bar{opacity:1;height:2px}.📖-measure-area{position:fixed;background:#f4f4f4;padding:50px 20px;z-index:2;visibility:hidden;left:0;right:0;bottom:0}.📖-measure-area .📖-page{margin:0 auto 50px}.📖-is-overflowing{border-bottom:1px solid #f0f}.📖-print-page{margin:0 auto}.📖-error{font:16px/1.4 -apple-system,BlinkMacSystemFont,Roboto,sans-serif;padding:15vh 15vw;z-index:3;position:fixed;top:0;left:0;right:0;bottom:0;background:hsla(0,0%,96%,.7)}.📖-error-title{font-size:1.5em;margin-bottom:16px}.📖-error-text{margin-bottom:16px;white-space:pre-line}.📖-error-footer{opacity:.5;font-size:.66em;text-transform:uppercase;letter-spacing:.02em}.📖-show-bleed .📖-print-page{background:#fff;outline:1px solid rgba(0,0,0,.1);box-shadow:0 1px 3px rgba(0,0,0,.2);margin:20px auto}.📖-placeholder-pulse{animation:pulse 1s infinite}}@keyframes pulse{0%{opacity:.2}50%{opacity:.5}to{opacity:.2}}@page{margin:0}@media print{.📖-root *{-webkit-print-color-adjust:exact;color-adjust:exact}.📖-controls,.📖-viewing>:not(.📖-root){display:none!important}.📖-print-page{padding:1px;margin:0 auto}.📖-zoom-wrap[style]{transform:none!important}}body.📖-viewing{margin:0}.📖-zoom-wrap{transform-origin:top left;transform-style:preserve-3d;height:calc(100vh - 120px)}[bindery-view-mode=interactive] .📖-zoom-wrap{transform-origin:center left}.📖-viewing>:not(.📖-root):not(.📖-measure-area){display:none!important}.📖-print-page{page-break-after:always;overflow:hidden;align-items:center;transition:all .2s}.📖-print-page,.📖-spread-wrapper{position:relative;display:flex;justify-content:center}.📖-spread-wrapper{margin:0 auto 32px}.📖-print-page .📖-spread-wrapper{margin:0 auto}.📖-flap-holder{perspective:5000px;position:absolute;top:0;right:0;left:0;bottom:0;margin:auto;transform-style:preserve-3d}.📖-flip-sizer{position:relative;margin:auto;padding:0 20px;box-sizing:content-box;height:100%!important}.📖-page3d{margin:auto;width:var(--bindery-page-width);height:var(--bindery-page-height);transform:rotateY(0);transform-style:preserve-3d;transform-origin:left;transition:transform .5s,box-shadow .1s;position:absolute;left:0;right:0;top:0;bottom:0}.📖-page3d:hover{box-shadow:2px 0 4px rgba(0,0,0,.2)}.📖-page3d.flipped{transform:rotateY(-180deg)}.📖-page3d .📖-page{position:absolute;backface-visibility:hidden;-webkit-backface-visibility:hidden;box-shadow:none}.📖-page3d .📖-page3d-front{transform:rotateY(0)}.📖-page3d .📖-page3d-back{transform:rotateY(-180deg)}.📖-print-mark-wrap{display:none;position:absolute;pointer-events:none;top:0;bottom:0;left:0;right:0;z-index:3}.📖-show-bleed-marks .📖-print-mark-wrap,.📖-show-bleed-marks .📖-print-mark-wrap>[class*=bleed],.📖-show-crop .📖-print-mark-wrap,.📖-show-crop .📖-print-mark-wrap>[class*=crop]{display:block}.📖-print-mark-wrap>div{display:none;position:absolute;overflow:hidden}.📖-print-mark-wrap>div:after,.📖-print-mark-wrap>div:before{content:"";display:block;position:absolute}.📖-print-mark-wrap>div:before{top:0;left:0}.📖-print-mark-wrap>div:after{bottom:0;right:0}.📖-bleed-left,.📖-bleed-right,.📖-crop-fold,.📖-crop-left,.📖-crop-right{width:1px;margin:auto}.📖-bleed-left:after,.📖-bleed-left:before,.📖-bleed-right:after,.📖-bleed-right:before,.📖-crop-fold:after,.📖-crop-fold:before,.📖-crop-left:after,.📖-crop-left:before,.📖-crop-right:after,.📖-crop-right:before{width:1px;height:12pt;background-image:linear-gradient(90deg,#000 0,#000 51%,transparent 0);background-size:1px 100%}.📖-bleed-bottom,.📖-bleed-top,.📖-crop-bottom,.📖-crop-top{height:1px}.📖-bleed-bottom:after,.📖-bleed-bottom:before,.📖-bleed-top:after,.📖-bleed-top:before,.📖-crop-bottom:after,.📖-crop-bottom:before,.📖-crop-top:after,.📖-crop-top:before{width:12pt;height:1px;background-image:linear-gradient(180deg,#000 0,#000 51%,transparent 0);background-size:100% 1px}.📖-crop-fold{right:0;left:0}.📖-crop-left{left:0}.📖-crop-right{right:0}.📖-crop-top{top:0}.📖-crop-bottom{bottom:0}.📖-print-meta{padding:12pt;text-align:center;font-family:-apple-system,BlinkMacSystemFont,Roboto,sans-serif;font-size:8pt;display:block!important;position:absolute;bottom:-60pt;left:0;right:0}');var je=function(){function i(){var r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};n(this,i),console.log("📖 Bindery "+e),this.autorun=r.autorun||!0,this.autoupdate=r.autoupdate||!1,x.isDebugging=r.debug||v("debug")||!1,b.validate(r,{name:"makeBook",autorun:b.bool,content:b.any,ControlsComponent:b.any,pageSetup:b.shape({name:"pageSetup",bleed:b.length,margin:b.shape({name:"margin",top:b.length,inner:b.length,outer:b.length,bottom:b.length}),size:b.shape({name:"size",width:b.length,height:b.length})}),view:b.enum.apply(b,a(Object.values($))),printSetup:b.shape({name:"printSetup",layout:b.enum.apply(b,a(Object.values(ee))),marks:b.enum.apply(b,a(Object.values(te))),paper:b.enum.apply(b,a(Object.values(Q)))}),rules:b.array}),this.pageSetup=new se(r.pageSetup),this.pageSetup.setupPaper(r.printSetup);var o=r.printSetup?r.printSetup.layout||ee.PAGES:ee.PAGES,s=r.printSetup?r.printSetup.marks||te.CROP:te.CROP;if(this.viewer=new Se({bindery:this,mode:r.view||$.PREVIEW,marks:s,layout:o,ControlsComponent:r.ControlsComponent}),this.rules=Me,r.rules&&this.addRules(r.rules),r.content)if("string"==typeof r.content){if(this.source=document.querySelector(r.content),!(this.source instanceof HTMLElement))return this.viewer.displayError("Content not specified",'Could not find element that matches selector "'+r.content+'"'),void console.error('Bindery: Could not find element that matches selector "'+r.content+'"');this.autorun&&this.makeBook()}else if("object"===t(r.content)&&r.content.url){var l=r.content.url,u=r.content.selector;this.fetchSource(l,u)}else r.content instanceof HTMLElement?(this.source=r.content,this.autorun&&this.makeBook()):console.error("Bindery: Source must be an element or selector");else this.viewer.displayError("Content not specified","You must include a source element, selector, or url"),console.error("Bindery: You must include a source element or selector")}return r(i,[{key:"fetchSource",value:function(e,t){var n=this;fetch(e).then(function(t){if(404===t.status)n.viewer.displayError("404",'Could not find file at "'+e+'"');else if(200===t.status)return t.text();return""}).then(function(e){var r=document.createElement("div");if(r.innerHTML=e,n.source=r.querySelector(t),!(n.source instanceof HTMLElement))return n.viewer.displayError("Source not specified",'Could not find element that matches selector "'+t+'"'),void console.error('Bindery: Could not find element that matches selector "'+t+'"');n.autorun&&n.makeBook()}).catch(function(t){console.error(t),"file"===window.location.href.split("://")[0]&&n.viewer.displayError("Can't fetch content from \""+e+'"',"Web pages can't fetch content unless they are on a server.")})}},{key:"cancel",value:function(){this.viewer.cancel(),document.body.classList.remove(k("viewing")),this.source.style.display=""}},{key:"addRules",value:function(e){var t=this;e.forEach(function(e){if(!(e instanceof Le.Rule))throw Error("Bindery: The following is not an instance of Bindery.Rule and will be ignored: "+e);t.rules.push(e)})}},{key:"updateBookSilent",value:function(){var e=this;this.layoutComplete=!1,this.source.style.display="";var t=this.source.cloneNode(!0);this.source.style.display="none",document.body.classList.add(k("viewing")),this.pageSetup.updateStylesheet(),X({content:t,rules:this.rules,success:function(t){e.viewer.book=t,e.viewer.render(),e.layoutComplete=!0},progress:function(){},error:function(t){e.layoutComplete=!0,e.viewer.displayError("Layout failed",t)}})}},{key:"makeBook",value:function(e){var t=this;if(this.source){if(this.layoutComplete=!1,!this.pageSetup.isSizeValid())return this.viewer.displayError("Page is too small","Size: "+JSON.stringify(this.pageSize)+" \n Margin: "+JSON.stringify(this.pageMargin)+" \n Try adjusting the sizes or units."),void console.error("Bindery: Cancelled pagination. Page is too small.");this.source.style.display="";var n=this.source.cloneNode(!0);this.source.style.display="none",this.viewer.clear(),document.body.classList.add(k("viewing")),x.isDebugging&&document.body.classList.add(k("debug")),this.pageSetup.updateStylesheet(),this.viewer.setInProgress(),X(n,this.rules).progress(function(e){t.viewer.renderProgress(e)}).then(function(n){t.viewer.book=n,t.viewer.render(),t.layoutComplete=!0,e&&e(),t.viewer.element.classList.remove(k("in-progress")),document.body.classList.remove(k("debug"))}).catch(function(e){t.layoutComplete=!0,t.viewer.element.classList.remove(k("in-progress")),t.viewer.displayError("Layout couldn't complete",e)})}else document.body.classList.add(k("viewing"))}}],[{key:"makeBook",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return e.autorun=!e.autorun||e.autorun,new i(e)}}]),i}();je.version=e;var De=Object.assign(je,Le);return De.View=$,De.Paper=Q,De.Layout=ee,De.Marks=te,De}();
+//# sourceMappingURL=bindery.min.js.map
diff --git a/libreto/assets/js/bindery.umd.js b/libreto/assets/js/bindery.umd.js
new file mode 100644
index 0000000..b2b11c4
--- /dev/null
+++ b/libreto/assets/js/bindery.umd.js
@@ -0,0 +1,3142 @@
+/* 📖 Bindery v2.1.0 */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.Bindery = factory());
+}(this, (function () { 'use strict';
+
+var BINDERY_VERSION = 'v2.1.0'
+
+function ___$insertStyle(css) {
+ if (!css) {
+ return;
+ }
+ if (typeof window === 'undefined') {
+ return;
+ }
+
+ var style = document.createElement('style');
+
+ style.setAttribute('type', 'text/css');
+ style.innerHTML = css;
+ document.head.appendChild(style);
+
+ return css;
+}
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+ return typeof obj;
+} : function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+};
+
+
+
+
+
+
+
+
+
+
+
+var classCallCheck = function (instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+};
+
+var createClass = function () {
+ function defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ("value" in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+
+ return function (Constructor, protoProps, staticProps) {
+ if (protoProps) defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) defineProperties(Constructor, staticProps);
+ return Constructor;
+ };
+}();
+
+
+
+
+
+
+
+var get = function get(object, property, receiver) {
+ if (object === null) object = Function.prototype;
+ var desc = Object.getOwnPropertyDescriptor(object, property);
+
+ if (desc === undefined) {
+ var parent = Object.getPrototypeOf(object);
+
+ if (parent === null) {
+ return undefined;
+ } else {
+ return get(parent, property, receiver);
+ }
+ } else if ("value" in desc) {
+ return desc.value;
+ } else {
+ var getter = desc.get;
+
+ if (getter === undefined) {
+ return undefined;
+ }
+
+ return getter.call(receiver);
+ }
+};
+
+var inherits = function (subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+ }
+
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
+ constructor: {
+ value: subClass,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+};
+
+
+
+
+
+
+
+
+
+
+
+var possibleConstructorReturn = function (self, call) {
+ if (!self) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }
+
+ return call && (typeof call === "object" || typeof call === "function") ? call : self;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+var toConsumableArray = function (arr) {
+ if (Array.isArray(arr)) {
+ for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
+
+ return arr2;
+ } else {
+ return Array.from(arr);
+ }
+};
+
+var Book = function () {
+ function Book() {
+ classCallCheck(this, Book);
+
+ this.pages = [];
+ this.queued = [];
+ this.isComplete = false;
+ this.estimatedProgress = 0;
+ }
+
+ createClass(Book, [{
+ key: "pagesForSelector",
+
+
+ // arguments: selector : String
+ // return: pages : [ Int ]
+ // if no matches: []
+ value: function pagesForSelector(sel) {
+ return this.pagesForTest(function (page) {
+ return page.element.querySelector(sel);
+ });
+ }
+ // arguments: testFunc : (element) => bool
+ // return: pages : [ Int ]
+ // if no matches: []
+
+ }, {
+ key: "pagesForTest",
+ value: function pagesForTest(testFunc) {
+ return this.pages.filter(function (pg) {
+ return testFunc(pg.element);
+ }).map(function (pg) {
+ return pg.number;
+ });
+ }
+ }, {
+ key: "onComplete",
+ value: function onComplete(func) {
+ if (!this.isComplete) this.queued.push(func);else func();
+ }
+ }, {
+ key: "setCompleted",
+ value: function setCompleted() {
+ this.isComplete = true;
+ this.estimatedProgress = 1;
+ this.queued.forEach(function (func) {
+ func();
+ });
+ this.queued = [];
+ }
+ }, {
+ key: "pageCount",
+ get: function get$$1() {
+ return this.pages.length;
+ }
+ }]);
+ return Book;
+}();
+
+var last = function last(arr) {
+ return arr[arr.length - 1];
+};
+
+var makeRanges = function makeRanges(arr) {
+ var str = '';
+ var prevNum = arr[0];
+ var isInARange = false;
+ arr.forEach(function (num, i) {
+ var isLast = i === arr.length - 1;
+ var isAdjacent = num === prevNum + 1;
+
+ if (i === 0) {
+ str += '' + num;
+ } else if (isLast) {
+ if (isAdjacent) {
+ str += '\u2013' + num;
+ } else if (isInARange) {
+ str += '\u2013' + prevNum + ', ' + num;
+ } else {
+ str += ', ' + num;
+ }
+ } else if (isAdjacent) {
+ isInARange = true;
+ } else if (isInARange && !isAdjacent) {
+ isInARange = false;
+ str += '\u2013' + prevNum + ', ' + num;
+ } else {
+ str += ', ' + num;
+ }
+ prevNum = num;
+ });
+ // return `${str} - [${arr}]`;
+ return str;
+};
+
+var cssNumberRegEx = /^([+-]?[0-9]+(.?[0-9]+)?)(px|in|cm|mm|pt|pc)$/;
+
+var isValidLength = function isValidLength(str) {
+ return cssNumberRegEx.test(str);
+};
+var isValidSize = function isValidSize(size) {
+ Object.keys(size).forEach(function (k) {
+ if (!isValidLength(size[k])) {
+ if (typeof size[k] === 'number') {
+ throw Error('Size is missing units { ' + k + ': ' + size[k] + ' }');
+ } else {
+ throw Error('Invalid size { ' + k + ': ' + size[k] + ' }');
+ }
+ }
+ });
+};
+
+var parseVal = function parseVal(str) {
+ var matches = str.match(cssNumberRegEx);
+ return {
+ val: Number(matches[1]),
+ unit: matches[3]
+ };
+};
+
+var validate = function validate(opts, validOpts) {
+ var isValid = true;
+ Object.keys(opts).forEach(function (k) {
+ if (!validOpts[k]) {
+ console.error('Bindery: \'' + validOpts.name + '\' doesn\'t have property \'' + k + '\'');
+ isValid = false;
+ } else {
+ var val = opts[k];
+ var checker = validOpts[k];
+ if (!checker(val)) {
+ console.error('Bindery: For property \'' + validOpts.name + '.' + k + '\', ' + JSON.stringify(val) + ' is not a valid value of type ' + checker.name);
+ isValid = false;
+ }
+ }
+ });
+ return isValid;
+};
+
+var isObj = function isObj(val) {
+ return (typeof val === 'undefined' ? 'undefined' : _typeof(val)) === 'object';
+};
+var isFunc = function isFunc(val) {
+ return typeof val === 'function';
+};
+var isBool = function isBool(val) {
+ return typeof val === 'boolean';
+};
+var isStr = function isStr(val) {
+ return typeof val === 'string';
+};
+var isArr = function isArr(val) {
+ return Array.isArray(val);
+};
+
+var OptionType = {
+ enum: function _enum() {
+ for (var _len = arguments.length, enumCases = Array(_len), _key = 0; _key < _len; _key++) {
+ enumCases[_key] = arguments[_key];
+ }
+
+ var enumCheck = function enumCheck(str) {
+ return enumCases.includes(str);
+ };
+ Object.defineProperty(enumCheck, 'name', { writable: true });
+ enumCheck.name = 'enum ( \'' + enumCases.join('\' | \'') + '\' )';
+ return enumCheck;
+ },
+ any: function any() {
+ return true;
+ },
+
+ string: isStr,
+ length: isValidLength,
+ bool: isBool,
+ func: isFunc,
+ obj: isObj,
+ array: isArr,
+ shape: function shape(validShape) {
+ return function (userShape) {
+ return isObj(userShape) && validate(userShape, validShape);
+ };
+ },
+
+ validate: validate
+};
+
+// via https://css-tricks.com/snippets/javascript/get-url-variables/
+var urlQuery = (function (variable) {
+ var query = window.location.search.substring(1);
+ var vars = query.split('&');
+ for (var i = 0; i < vars.length; i += 1) {
+ var pair = vars[i].split('=');
+ if (pair[0] === variable) {
+ return pair[1];
+ }
+ }
+ return false;
+});
+
+// const p = 'bindery-';
+var p = '📖-';
+
+var prefix = function prefix(str) {
+ return '' + p + str;
+};
+var prefixClass = function prefixClass(str) {
+ return '.' + prefix(str);
+};
+
+var c = function c(str) {
+ if (str[0] === '.') {
+ return prefixClass(str.substr(1));
+ }
+ return prefix(str);
+};
+
+// Small utility to create div with namespaced classes
+var el = function el(className) {
+ var content = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
+
+ var div = document.createElement('div');
+ div.className = className.split('.').filter(function (txt) {
+ return txt !== '';
+ }).map(c).join(' ');
+
+ if (typeof content === 'string') {
+ div.textContent = content;
+ } else if (Array.isArray(content)) {
+ content.forEach(function (child) {
+ return div.appendChild(child);
+ });
+ }
+ return div;
+};
+
+var Page = function () {
+ function Page() {
+ classCallCheck(this, Page);
+
+ this.flowContent = el('content');
+ this.flowBox = el('flowbox', [this.flowContent]);
+ this.footer = el('footer');
+ this.background = el('background');
+ this.element = el('page', [this.background, this.flowBox, this.footer]);
+ }
+
+ createClass(Page, [{
+ key: 'overflowAmount',
+ value: function overflowAmount() {
+ var contentH = this.flowContent.offsetHeight;
+ var boxH = this.flowBox.offsetHeight;
+
+ if (boxH === 0) {
+ throw Error('Bindery: Trying to flow into a box of zero height.');
+ }
+
+ return contentH - boxH;
+ }
+ }, {
+ key: 'hasOverflowed',
+ value: function hasOverflowed() {
+ return this.overflowAmount() > -5;
+ }
+ }, {
+ key: 'setLeftRight',
+ value: function setLeftRight(dir) {
+ if (dir === 'left') {
+ this.side = dir;
+ this.element.classList.remove(c('right'));
+ this.element.classList.add(c('left'));
+ } else if (dir === 'right') {
+ this.side = dir;
+ this.element.classList.remove(c('left'));
+ this.element.classList.add(c('right'));
+ } else {
+ throw Error('Bindery: Setting page to invalid direction' + dir);
+ }
+ }
+ }, {
+ key: 'setPreference',
+ value: function setPreference(dir) {
+ if (dir === 'left') this.alwaysLeft = true;
+ if (dir === 'right') this.alwaysRight = true;
+ }
+ }, {
+ key: 'suppressErrors',
+ get: function get$$1() {
+ return this.suppress || false;
+ },
+ set: function set$$1(newVal) {
+ this.suppress = newVal;
+ if (newVal) {
+ this.element.classList.add(c('is-overflowing'));
+ } else {
+ this.element.classList.remove(c('is-overflowing'));
+ }
+ }
+ }, {
+ key: 'isEmpty',
+ get: function get$$1() {
+ return !this.hasOutOfFlowContent && this.flowContent.textContent.trim() === '' && this.flowContent.offsetHeight < 1;
+ }
+ }, {
+ key: 'isLeft',
+ get: function get$$1() {
+ return this.side === 'left';
+ }
+ }, {
+ key: 'isRight',
+ get: function get$$1() {
+ return this.side === 'right';
+ }
+ }], [{
+ key: 'isSizeValid',
+ value: function isSizeValid() {
+ document.body.classList.remove(c('viewing'));
+
+ var testPage = new Page();
+ var measureArea = document.querySelector(c('.measure-area'));
+ if (!measureArea) measureArea = document.body.appendChild(el('measure-area'));
+
+ measureArea.innerHTML = '';
+ measureArea.appendChild(testPage.element);
+ var box = testPage.flowBox.getBoundingClientRect();
+
+ measureArea.parentNode.removeChild(measureArea);
+
+ return box.height > 100 && box.width > 100; // TODO: Number is arbitrary
+ }
+ }]);
+ return Page;
+}();
+
+// TODO: Combine isSplittable and shouldIgnoreOverflow
+// Walk up the tree to see if we are within
+// an overflow-ignoring node
+var shouldIgnoreOverflow = function shouldIgnoreOverflow(element) {
+ if (element.hasAttribute('data-ignore-overflow')) return true;
+ if (element.parentElement) return shouldIgnoreOverflow(element.parentElement);
+ return false;
+};
+
+// When there is no debugDelay,
+// the throttler will occassionally use rAF
+// to prevent stack overflow
+// and browser lockup
+
+var MAX_CALLS = 800;
+var MAX_TIME = 50; // ms
+
+var Scheduler = function () {
+ function Scheduler() {
+ classCallCheck(this, Scheduler);
+
+ this.numberOfCalls = 0;
+ this.resumeLimit = Infinity;
+ this.callsSinceResume = 0;
+ this.queuedFunc = null;
+ this.isPaused = false;
+ this.useDelay = false;
+ this.delayTime = 100;
+
+ this.lastWaitedTime = 0;
+ }
+
+ createClass(Scheduler, [{
+ key: 'throttle',
+ value: function throttle(func) {
+ var _this = this;
+
+ this.callsSinceResume += 1;
+
+ if (this.callsSinceResume > this.resumeLimit) {
+ this.endResume();
+ }
+
+ var handlerTime = performance.now() - this.lastWaitedTime;
+
+ if (this.isPaused) {
+ this.queuedFunc = func;
+ } else if (this.useDelay) {
+ setTimeout(func, this.delayTime);
+ } else if (this.numberOfCalls < MAX_CALLS && handlerTime < MAX_TIME) {
+ this.numberOfCalls += 1;
+ func();
+ } else {
+ this.numberOfCalls = 0;
+ if (document.hidden) {
+ // Tab in background
+ setTimeout(func, 1);
+ } else {
+ requestAnimationFrame(function (t) {
+ _this.lastWaitedTime = t;
+ func();
+ });
+ }
+ }
+ }
+ }, {
+ key: 'pause',
+ value: function pause() {
+ if (this.isPaused) return 'Already paused';
+ this.isPaused = true;
+ return 'Paused';
+ }
+ }, {
+ key: 'resumeDelay',
+ value: function resumeDelay() {
+ this.useDelay = true;
+ this.resume();
+ }
+ }, {
+ key: 'finish',
+ value: function finish() {
+ this.useDelay = false;
+ this.resume();
+ }
+ }, {
+ key: 'resume',
+ value: function resume() {
+ if (this.isPaused) {
+ this.isPaused = false;
+ if (this.queuedFunc) {
+ this.queuedFunc();
+ this.queuedFunc = null;
+ } else {
+ return 'Layout complete';
+ }
+ return 'Resuming';
+ }
+ return 'Already running';
+ }
+ }, {
+ key: 'step',
+ value: function step() {
+ if (!this.isPaused) {
+ return this.pause();
+ }
+ if (this.queuedFunc) {
+ var queued = this.queuedFunc;
+ var n = queued.name;
+ this.queuedFunc = null;
+ queued();
+ return n;
+ }
+ return 'Layout complete';
+ }
+ }, {
+ key: 'resumeFor',
+ value: function resumeFor(n) {
+ this.callsSinceResume = 0;
+ this.resumeLimit = n;
+ return this.resume();
+ }
+ }, {
+ key: 'endResume',
+ value: function endResume() {
+ console.log('Paused after ' + this.resumeLimit);
+ this.resumeLimit = Infinity;
+ this.callsSinceResume = 0;
+ this.pause();
+ }
+ }, {
+ key: 'isDebugging',
+ get: function get$$1() {
+ return this.useDelay;
+ },
+ set: function set$$1(newValue) {
+ this.useDelay = newValue;
+ if (newValue) {
+ window.binderyDebug = {
+ pause: this.pause.bind(this),
+ resume: this.resume.bind(this),
+ resumeFor: this.resumeFor.bind(this),
+ step: this.step.bind(this),
+ finish: this.finish.bind(this)
+ };
+ console.log('Bindery: Debug layout with the following: \nbinderyDebug.pause() \nbinderyDebug.resume()\n binderyDebug.resumeFor(n) // pauses after n steps, \nbinderyDebug.step()');
+ }
+ }
+ }]);
+ return Scheduler;
+}();
+
+var scheduler = new Scheduler();
+
+// promise-inspired thenable.
+// really just to make callbacks cleaner,
+// not guarenteed to be asynchronous.
+// (if then and catch aren't registered yet,
+// it waits until they are).
+
+var Thenable = function () {
+ function Thenable(func) {
+ classCallCheck(this, Thenable);
+
+ this.successArgs = [];
+ this.errorArgs = [];
+
+ this.successCallback = null;
+ this.errorCallback = null;
+ this.progressCallback = null;
+
+ this.chainedThenable = null;
+ this.chainedSuccessCallback = null;
+ this.chainedErrorCallback = null;
+
+ this.isRejected = false;
+ this.isResolved = false;
+
+ this.resolve = this.resolve.bind(this);
+ this.reject = this.reject.bind(this);
+ this.updateProgress = this.updateProgress.bind(this);
+
+ if (func) func(this.resolve, this.reject, this.updateProgress);
+ }
+
+ createClass(Thenable, [{
+ key: 'then',
+ value: function then(func) {
+ if (this.chainedThenable) {
+ return this.chainedThenable.then(func);
+ }
+
+ if (!this.successCallback) {
+ this.successCallback = func;
+ if (this.isResolved) this.resolve();
+ return this;
+ } else if (!this.chainedSuccessCallback) {
+ this.chainedSuccessCallback = func;
+ if (this.isResolved) this.resolveChained();
+ // console.log('attached chained then');
+ return this;
+ }
+ throw Error('need real chained then');
+ }
+ }, {
+ key: 'progress',
+ value: function progress(func) {
+ this.progressCallback = func;
+ return this;
+ }
+ }, {
+ key: 'catch',
+ value: function _catch(func) {
+ if (this.chainedThenable) {
+ return this.chainedThenable.catch(func);
+ }
+
+ if (!this.errorCallback) {
+ this.errorCallback = func;
+ if (this.isRejected) this.reject();
+ return this;
+ } else if (!this.chainedErrorCallback) {
+ this.chainedErrorCallback = func;
+ if (this.isRejected) this.rejectChained();
+ // console.log('attached chained error');
+ return this;
+ }
+ throw Error('need real chained catch');
+ }
+ }, {
+ key: 'resolve',
+ value: function resolve() {
+ this.isResolved = true;
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ if (args.length > 0) this.successArgs = args;
+ if (this.successCallback !== null) {
+ // console.log('applying then');
+ var result = void 0;
+ if (this.successArgs.length > 0) {
+ result = this.successCallback.apply(this, toConsumableArray(this.successArgs));
+ } else {
+ result = this.successCallback();
+ }
+ if (result && result.then) this.chainedThenable = result;
+ if (this.chainedSuccessCallback) this.resolveChained();
+ } else {
+ // console.log('waiting for then');
+ }
+ }
+ }, {
+ key: 'resolveChained',
+ value: function resolveChained() {
+ if (this.chainedThenable) this.chainedThenable.then(this.chainedSuccessCallback);else this.chainedSuccessCallback();
+ }
+ }, {
+ key: 'reject',
+ value: function reject() {
+ this.isRejected = true;
+
+ for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+ args[_key2] = arguments[_key2];
+ }
+
+ if (args.length > 0) this.errorArgs = args;
+ if (this.errorCallback !== null) {
+ // console.log('applying catch');
+ var result = void 0;
+ if (this.errorArgs.length > 0) {
+ result = this.errorCallback.apply(this, toConsumableArray(this.errorArgs));
+ } else {
+ result = this.errorCallback();
+ }
+ if (result && result.then) {
+ this.chainedThenable = result;
+ }
+ if (this.successCallback) {
+ if (result && result.then) {
+ // console.log('forwarded unused then');
+ result.then(this.successCallback);
+ }
+ }
+ if (this.chainedSuccessCallback) {
+ if (result && result.then) {
+ // console.log('forwarded chained then');
+ result.then(this.chainedSuccessCallback);
+ } else this.chainedSuccessCallback();
+ }
+ if (this.chainedErrorCallback) this.rejectChained();
+ } else {
+ // console.log('waiting for catch');
+ }
+ }
+ }, {
+ key: 'rejectChained',
+ value: function rejectChained() {
+ if (this.chainedThenable) this.chainedThenable.catch(this.chainedErrorCallback);else this.chainedErrorCallback();
+ }
+ }, {
+ key: 'updateProgress',
+ value: function updateProgress() {
+ if (this.progressCallback !== null) {
+ this.progressCallback.apply(this, arguments);
+ }
+ }
+ }], [{
+ key: 'resolved',
+ value: function resolved() {
+ return new Thenable(function (resolve) {
+ return resolve();
+ });
+ }
+ }]);
+ return Thenable;
+}();
+
+// Try adding a text node in one go
+var addTextNode = function addTextNode(textNode, parent, page) {
+ return new Thenable(function (resolve, reject) {
+ parent.appendChild(textNode);
+
+ if (page.hasOverflowed()) {
+ parent.removeChild(textNode);
+ scheduler.throttle(reject);
+ } else {
+ scheduler.throttle(resolve);
+ }
+ });
+};
+
+// Try adding a text node by incrementally adding words
+// until it just barely doesnt overflow.
+// Binary search would probably be better but its not currenty
+// the bottleneck.
+var addTextNodeIncremental = function addTextNodeIncremental(textNode, parent, page) {
+ return new Thenable(function (resolve, reject) {
+ var originalText = textNode.nodeValue;
+ parent.appendChild(textNode);
+
+ if (!page.hasOverflowed() || shouldIgnoreOverflow(parent)) {
+ scheduler.throttle(resolve);
+ return;
+ }
+
+ var pos = 0;
+
+ var splitTextStep = function splitTextStep() {
+ textNode.nodeValue = originalText.substr(0, pos);
+
+ if (page.hasOverflowed()) {
+ // Back out to word boundary
+ if (originalText.charAt(pos) === ' ') pos -= 1; // TODO: redundant
+ while (originalText.charAt(pos) !== ' ' && pos > 0) {
+ pos -= 1;
+ }if (pos < 1) {
+ textNode.nodeValue = originalText;
+ textNode.parentNode.removeChild(textNode);
+ scheduler.throttle(reject);
+ return;
+ }
+
+ // console.log(`Text breaks at ${pos}: ${originalText.substr(0, pos)}`);
+
+ var fittingText = originalText.substr(0, pos);
+ var overflowingText = originalText.substr(pos);
+ textNode.nodeValue = fittingText;
+
+ // Start on new page
+ var remainingTextNode = document.createTextNode(overflowingText);
+ scheduler.throttle(function () {
+ return resolve(remainingTextNode);
+ });
+ return;
+ }
+ if (pos > originalText.length - 1) {
+ scheduler.throttle(resolve);
+ return;
+ }
+
+ pos += 1;
+ while (originalText.charAt(pos) !== ' ' && pos < originalText.length) {
+ pos += 1;
+ }scheduler.throttle(splitTextStep);
+ };
+
+ splitTextStep();
+ });
+};
+
+var Rule = function Rule(options) {
+ var _this = this;
+
+ classCallCheck(this, Rule);
+
+ this.name = options.name ? options.name : 'Unnamed Bindery Rule';
+ this.selector = '';
+
+ Object.keys(options).forEach(function (key) {
+ _this[key] = options[key];
+ });
+};
+
+var OutOfFlow = function (_Rule) {
+ inherits(OutOfFlow, _Rule);
+
+ function OutOfFlow(options) {
+ classCallCheck(this, OutOfFlow);
+
+ var _this = possibleConstructorReturn(this, (OutOfFlow.__proto__ || Object.getPrototypeOf(OutOfFlow)).call(this, options));
+
+ _this.name = 'Out of Flow';
+ return _this;
+ }
+
+ createClass(OutOfFlow, [{
+ key: 'beforeAdd',
+ value: function beforeAdd(elmt) {
+ // Avoid breaking inside this element. Once it's completely added,
+ // it will moved onto the background layer.
+
+ elmt.setAttribute('data-ignore-overflow', true);
+ return elmt;
+ }
+ }, {
+ key: 'afterAdd',
+ value: function afterAdd(elmt, book, continueOnNewPage, makeNewPage) {
+ this.createOutOfFlowPages(elmt, book, makeNewPage);
+
+ // Catches cases when we didn't need to create a new page. but unclear
+ if (this.continue !== 'same' || book.pageInProgress.hasOutOfFlowContent) {
+ continueOnNewPage(true);
+ if (this.continue === 'left' || this.continue === 'right') {
+ book.pageInProgress.setPreference(this.continue);
+ }
+ }
+
+ return elmt;
+ }
+ }]);
+ return OutOfFlow;
+}(Rule);
+
+// Options:
+// selector: String
+
+var FullBleedPage = function (_OutOfFlow) {
+ inherits(FullBleedPage, _OutOfFlow);
+
+ function FullBleedPage(options) {
+ classCallCheck(this, FullBleedPage);
+
+ options.continue = options.continue || 'same';
+ options.rotate = options.rotate || 'none';
+
+ var _this = possibleConstructorReturn(this, (FullBleedPage.__proto__ || Object.getPrototypeOf(FullBleedPage)).call(this, options));
+
+ OptionType.validate(options, {
+ name: 'FullBleedPage',
+ selector: OptionType.string,
+ continue: OptionType.enum('next', 'same', 'left', 'right'),
+ rotate: OptionType.enum('none', 'inward', 'outward', 'clockwise', 'counterclockwise')
+ });
+ return _this;
+ }
+
+ createClass(FullBleedPage, [{
+ key: 'createOutOfFlowPages',
+ value: function createOutOfFlowPages(elmt, book, makeNewPage) {
+ elmt.parentNode.removeChild(elmt);
+
+ var newPage = void 0;
+ if (book.pageInProgress.isEmpty) {
+ newPage = book.pageInProgress;
+ } else {
+ newPage = makeNewPage();
+ book.pages.push(newPage);
+ }
+ if (this.rotate !== 'none') {
+ var rotateContainer = el('.rotate-container.page-size-rotated.rotate-' + this.rotate);
+ rotateContainer.appendChild(newPage.background);
+ newPage.element.appendChild(rotateContainer);
+ }
+ newPage.background.appendChild(elmt);
+ newPage.hasOutOfFlowContent = true;
+ }
+ }]);
+ return FullBleedPage;
+}(OutOfFlow);
+
+// Options:
+// selector: String
+
+var FullBleedSpread = function (_OutOfFlow) {
+ inherits(FullBleedSpread, _OutOfFlow);
+
+ function FullBleedSpread(options) {
+ classCallCheck(this, FullBleedSpread);
+
+ options.continue = options.continue || 'same';
+ options.rotate = options.rotate || 'none';
+
+ var _this = possibleConstructorReturn(this, (FullBleedSpread.__proto__ || Object.getPrototypeOf(FullBleedSpread)).call(this, options));
+
+ OptionType.validate(options, {
+ name: 'FullBleedSpread',
+ selector: OptionType.string,
+ continue: OptionType.enum('next', 'same', 'left', 'right'),
+ rotate: OptionType.enum('none', 'clockwise', 'counterclockwise')
+ });
+ return _this;
+ }
+
+ createClass(FullBleedSpread, [{
+ key: 'createOutOfFlowPages',
+ value: function createOutOfFlowPages(elmt, book, makeNewPage) {
+ var _this2 = this;
+
+ elmt.parentNode.removeChild(elmt);
+
+ var leftPage = void 0;
+ if (book.pageInProgress.isEmpty) {
+ leftPage = book.pageInProgress;
+ } else {
+ leftPage = makeNewPage();
+ book.pages.push(leftPage);
+ }
+
+ var rightPage = makeNewPage();
+ book.pages.push(rightPage);
+
+ if (this.rotate !== 'none') {
+ [leftPage, rightPage].forEach(function (page) {
+ var rotateContainer = el('.rotate-container.spread-size-rotated.rotate-spread-' + _this2.rotate);
+ rotateContainer.appendChild(page.background);
+ page.element.appendChild(rotateContainer);
+ });
+ }
+
+ leftPage.background.appendChild(elmt);
+ leftPage.element.classList.add(c('spread'));
+ leftPage.setPreference('left');
+ leftPage.isOutOfFlow = this.continue === 'same';
+ leftPage.hasOutOfFlowContent = true;
+
+ rightPage.background.appendChild(elmt.cloneNode(true));
+ rightPage.element.classList.add(c('spread'));
+ rightPage.setPreference('right');
+ rightPage.isOutOfFlow = this.continue === 'same';
+ rightPage.hasOutOfFlowContent = true;
+ }
+ }]);
+ return FullBleedSpread;
+}(OutOfFlow);
+
+var PageBreak = function (_Rule) {
+ inherits(PageBreak, _Rule);
+
+ function PageBreak(options) {
+ classCallCheck(this, PageBreak);
+
+ options.position = options.position || 'before';
+ options.continue = options.continue || 'next';
+
+ var _this = possibleConstructorReturn(this, (PageBreak.__proto__ || Object.getPrototypeOf(PageBreak)).call(this, options));
+
+ OptionType.validate(options, {
+ name: 'PageBreak',
+ selector: OptionType.string,
+ continue: OptionType.enum('next', 'left', 'right'),
+ position: OptionType.enum('before', 'after', 'both', 'avoid')
+ });
+ return _this;
+ }
+
+ createClass(PageBreak, [{
+ key: 'beforeAdd',
+ value: function beforeAdd(elmt, book, continueOnNewPage) {
+ if (this.position === 'before' || this.position === 'both') {
+ if (!book.pageInProgress.isEmpty) {
+ continueOnNewPage();
+ }
+ if (this.continue !== 'next') {
+ book.pageInProgress.setPreference(this.continue);
+ }
+ }
+ return elmt;
+ }
+ }, {
+ key: 'afterAdd',
+ value: function afterAdd(elmt, book, continueOnNewPage) {
+ if (this.position === 'after' || this.position === 'both') {
+ var newPage = continueOnNewPage(true);
+ if (this.continue !== 'next') {
+ newPage.setPreference(this.continue);
+ }
+ }
+ return elmt;
+ }
+ }, {
+ key: 'avoidSplit',
+ get: function get$$1() {
+ return this.position === 'avoid';
+ }
+ }]);
+ return PageBreak;
+}(Rule);
+
+var elementToString = function elementToString(node) {
+ var tag = node.tagName.toLowerCase();
+ var id = node.id ? '#' + node.id : '';
+
+ var classes = '';
+ if (node.classList.length > 0) {
+ classes = '.' + [].concat(toConsumableArray(node.classList)).join('.');
+ }
+
+ var text = '';
+ if (id.length < 1 && classes.length < 2) {
+ text = '("' + node.textContent.substr(0, 30).replace(/\s+/g, ' ') + '...")';
+ }
+ return tag + id + classes + text;
+};
+
+var isFullPageRule = function isFullPageRule(rule) {
+ return rule instanceof FullBleedSpread || rule instanceof FullBleedPage || rule instanceof PageBreak;
+};
+
+var dedupe = function dedupe(inputRules) {
+ var conflictRules = inputRules.filter(isFullPageRule);
+ var uniqueRules = inputRules.filter(function (rule) {
+ return !conflictRules.includes(rule);
+ });
+
+ var firstSpreadRule = conflictRules.find(function (rule) {
+ return rule instanceof FullBleedSpread;
+ });
+ var firstPageRule = conflictRules.find(function (rule) {
+ return rule instanceof FullBleedPage;
+ });
+
+ // Only apply one fullpage or fullspread
+ if (firstSpreadRule) uniqueRules.push(firstSpreadRule);else if (firstPageRule) uniqueRules.push(firstPageRule);else uniqueRules.push.apply(uniqueRules, toConsumableArray(conflictRules)); // multiple pagebreaks are ok
+
+ return uniqueRules;
+};
+
+var RuleSet = function () {
+ function RuleSet(rules) {
+ classCallCheck(this, RuleSet);
+
+ this.rules = rules;
+ this.pageRules = rules.filter(function (r) {
+ return r.eachPage;
+ });
+ this.beforeAddRules = rules.filter(function (r) {
+ return r.selector && r.beforeAdd;
+ });
+ this.afterAddRules = rules.filter(function (r) {
+ return r.selector && r.afterAdd;
+ });
+ this.selectorsNotToSplit = rules.filter(function (rule) {
+ return rule.avoidSplit;
+ }).map(function (rule) {
+ return rule.selector;
+ });
+ }
+
+ createClass(RuleSet, [{
+ key: 'setup',
+ value: function setup() {
+ this.rules.forEach(function (rule) {
+ if (rule.setup) rule.setup();
+ });
+ }
+ }, {
+ key: 'startPage',
+ value: function startPage(pg, book) {
+ this.rules.forEach(function (rule) {
+ if (rule.afterPageCreated) rule.afterPageCreated(pg, book);
+ });
+ }
+ }, {
+ key: 'finishEveryPage',
+ value: function finishEveryPage(book) {
+ this.pageRules.forEach(function (rule) {
+ book.pages.forEach(function (page) {
+ rule.eachPage(page, book);
+ });
+ });
+ }
+ }, {
+ key: 'finishPage',
+ value: function finishPage(page, book) {
+ this.pageRules.forEach(function (rule) {
+ rule.eachPage(page, book);
+ });
+ }
+ }, {
+ key: 'beforeAddElement',
+ value: function beforeAddElement(element, book, continueOnNewPage, makeNewPage) {
+ var addedElement = element;
+
+ var matchingRules = this.beforeAddRules.filter(function (rule) {
+ return addedElement.matches(rule.selector);
+ });
+ // const uniqueRules = dedupeRules(matchingRules);
+
+ matchingRules.forEach(function (rule) {
+ addedElement = rule.beforeAdd(addedElement, book, continueOnNewPage, makeNewPage);
+ });
+ return addedElement;
+ }
+ }, {
+ key: 'afterAddElement',
+ value: function afterAddElement(originalElement, book, continueOnNewPage, makeNewPage, moveToNext) {
+ var addedElement = originalElement;
+
+ var matchingRules = this.afterAddRules.filter(function (rule) {
+ return addedElement.matches(rule.selector);
+ });
+ var uniqueRules = dedupe(matchingRules);
+
+ // TODO:
+ // While this does catch overflows, it introduces a few new bugs.
+ // It is pretty aggressive to move the entire node to the next page.
+ // - 1. there is no guarentee it will fit on the new page
+ // - 2. if it has childNodes, those side effects will not be undone,
+ // which means footnotes will get left on previous page.
+ // - 3. if it is a large paragraph, it will leave a large gap. the
+ // ideal approach would be to only need to invalidate
+ // the last line of text.
+
+ uniqueRules.forEach(function (rule) {
+ addedElement = rule.afterAdd(addedElement, book, continueOnNewPage, makeNewPage, function (problemElement) {
+ moveToNext(problemElement);
+ return rule.afterAdd(problemElement, book, continueOnNewPage, makeNewPage, function () {
+ console.log('Couldn\'t apply ' + rule.name + ' to ' + elementToString(problemElement) + '. Caused overflows twice.');
+ });
+ });
+ });
+ return addedElement;
+ }
+ }]);
+ return RuleSet;
+}();
+
+var indexOfNextInFlowPage = function indexOfNextInFlowPage(pages, startIndex) {
+ for (var i = startIndex; i < pages.length; i += 1) {
+ if (!pages[i].isOutOfFlow) {
+ return i;
+ }
+ }
+ return startIndex;
+};
+
+// Given an array of pages with alwaysLeft, alwaysRight, and isOutOfFlow
+// properties.
+//
+// Orders them so that alwaysLeft and alwaysRight are true.
+
+// If the page is 'in flow', order must be respected, so extra blank pages
+// are inserted.
+//
+// If the page is 'out of flow', we'd prefer not to add a blank page.
+// Instead it floats backwards in the book, pulling the next
+// in-flow page forward. If several 'out of flow' pages
+// are next to each other, they will remain in order, all being pushed
+// backward together.
+
+
+var orderPages = function orderPages(pages, makeNewPage) {
+ var orderedPages = pages.slice();
+
+ for (var i = 0; i < orderedPages.length; i += 1) {
+ var page = orderedPages[i];
+ var isLeft = i % 2 !== 0;
+
+ if (isLeft && page.alwaysRight || !isLeft && page.alwaysLeft) {
+ if (page.isOutOfFlow) {
+ var indexToSwap = indexOfNextInFlowPage(orderedPages, i + 1);
+ var pageToMoveUp = orderedPages[indexToSwap];
+ orderedPages.splice(indexToSwap, 1);
+ orderedPages.splice(i, 0, pageToMoveUp);
+ } else {
+ orderedPages.splice(i, 0, makeNewPage());
+ }
+ }
+ }
+ return orderedPages;
+};
+
+var annotatePages = function annotatePages(pages) {
+ // ———
+ // NUMBERING
+
+ // TODO: Pass in facingpages options
+ var facingPages = true;
+ if (facingPages) {
+ pages.forEach(function (page, i) {
+ page.number = i + 1;
+ page.setLeftRight(i % 2 === 0 ? 'right' : 'left');
+ });
+ } else {
+ pages.forEach(function (page) {
+ page.setLeftRight('right');
+ });
+ }
+
+ // ———
+ // RUNNING HEADERS
+
+ // Sections to annotate with.
+ // This should be a hierarchical list of selectors.
+ // Every time one is selected, it annotates all following pages
+ // and clears any subselectors.
+ // TODO: Make this configurable
+ var running = { h1: '', h2: '', h3: '', h4: '', h5: '', h6: '' };
+
+ pages.forEach(function (page) {
+ page.heading = {};
+ Object.keys(running).forEach(function (tagName, i) {
+ var element = page.element.querySelector(tagName);
+ if (element) {
+ running[tagName] = element.textContent;
+ // clear remainder
+ Object.keys(running).forEach(function (tag, j) {
+ if (j > i) running[tag] = '';
+ });
+ }
+ if (running[tagName] !== '') {
+ page.heading[tagName] = running[tagName];
+ }
+ });
+ });
+};
+
+// @param rules: array of Bindery.Rules
+// @return: A new function that clones the given
+// breadcrumb according to those rules. (original : Array) => clone : Array
+//
+// The breadcrumb is an array of nested elments,
+// for example .content > article > p > a).
+//
+// It's shallowly cloned every time we move to the next page,
+// to create the illusion that nodes are continuing from page
+// to page.
+//
+// The transition can be customized by setting a Split rule,
+// which lets you add classes to the original and cloned element
+// to customize styling.
+
+var breadcrumbClone = function breadcrumbClone(origBreadcrumb, rules) {
+ var newBreadcrumb = [];
+
+ // TODO check if element actually matches
+ var toNextClasses = rules.filter(function (rule) {
+ return rule.customToNextClass;
+ }).map(function (rule) {
+ return rule.customToNextClass;
+ });
+ var fromPrevClasses = rules.filter(function (rule) {
+ return rule.customFromPreviousClass;
+ }).map(function (rule) {
+ return rule.customFromPreviousClass;
+ });
+
+ var markAsToNext = function markAsToNext(node) {
+ node.classList.add(c('continues'));
+ toNextClasses.forEach(function (cl) {
+ return node.classList.add(cl);
+ });
+ };
+
+ var markAsFromPrev = function markAsFromPrev(node) {
+ node.classList.add(c('continuation'));
+ fromPrevClasses.forEach(function (cl) {
+ return node.classList.add(cl);
+ });
+ };
+
+ for (var i = origBreadcrumb.length - 1; i >= 0; i -= 1) {
+ var original = origBreadcrumb[i];
+ var clone = original.cloneNode(false); // shallow
+ clone.innerHTML = '';
+
+ markAsToNext(original);
+ markAsFromPrev(clone);
+
+ // Special case for ordered lists
+ if (clone.tagName === 'OL') {
+ // restart numbering
+ var prevStart = 1;
+ if (original.hasAttribute('start')) {
+ // the OL is also a continuation
+ prevStart = parseInt(original.getAttribute('start'), 10);
+ }
+ if (i < origBreadcrumb.length - 1 && origBreadcrumb[i + 1].tagName === 'LI') {
+ // the first list item is a continuation
+ prevStart -= 1;
+ }
+ var prevCount = original.children.length;
+ var newStart = prevStart + prevCount;
+ clone.setAttribute('start', newStart);
+ }
+
+ if (i < origBreadcrumb.length - 1) clone.appendChild(newBreadcrumb[i + 1]);
+ newBreadcrumb[i] = clone;
+ }
+
+ return newBreadcrumb;
+};
+
+// Note: Doesn't ever reject, since missing images
+// shouldn't prevent layout from resolving
+
+var waitForImage = function waitForImage(image) {
+ return new Thenable(function (resolve) {
+ var pollForSize = setInterval(function () {
+ if (image.naturalWidth) {
+ clearInterval(pollForSize);
+ resolve();
+ }
+ }, 10);
+
+ image.addEventListener('error', function () {
+ clearInterval(pollForSize);
+ resolve();
+ });
+ image.src = image.src;
+ });
+};
+
+// Bindery
+// paginate
+// Utils
+var MAXIMUM_PAGE_LIMIT = 2000;
+
+var isTextNode = function isTextNode(node) {
+ return node.nodeType === Node.TEXT_NODE;
+};
+var isElement = function isElement(node) {
+ return node.nodeType === Node.ELEMENT_NODE;
+};
+var isScript = function isScript(node) {
+ return node.tagName === 'SCRIPT';
+};
+var isImage = function isImage(node) {
+ return node.tagName === 'IMG';
+};
+var isUnloadedImage = function isUnloadedImage(node) {
+ return isImage(node) && !node.naturalWidth;
+};
+var isContent = function isContent(node) {
+ return isElement(node) && !isScript(node);
+};
+
+var sec = function sec(ms) {
+ return (ms / 1000).toFixed(2);
+};
+
+// Walk up the tree to see if we can safely
+// insert a split into this node.
+var isSplittable = function isSplittable(element, selectorsNotToSplit) {
+ if (selectorsNotToSplit.some(function (sel) {
+ return element.matches(sel);
+ })) {
+ if (element.hasAttribute('data-bindery-did-move') || element.classList.contains(c('continuation'))) {
+ return true; // ignore rules and split it anyways.
+ }
+ return false;
+ }
+ if (element.parentElement) {
+ return isSplittable(element.parentElement, selectorsNotToSplit);
+ }
+ return true;
+};
+
+var paginate$1 = function paginate(content, rules) {
+ return new Thenable(function (paginateResolve, paginateReject, progress) {
+ // SETUP
+ var layoutWaitingTime = 0;
+ var elementCount = 0;
+ var elementsProcessed = 0;
+
+ var ruleSet = new RuleSet(rules);
+ var measureArea = document.body.appendChild(el('.measure-area'));
+
+ var breadcrumb = []; // Keep track of position in original tree
+ var book = new Book();
+
+ var canSplit = function canSplit() {
+ return !shouldIgnoreOverflow(last(breadcrumb));
+ };
+
+ var makeNewPage = function makeNewPage() {
+ var newPage = new Page();
+ measureArea.appendChild(newPage.element);
+
+ ruleSet.startPage(newPage, book);
+ return newPage;
+ };
+
+ var finishPage = function finishPage(page, ignoreOverflow) {
+ if (page && page.hasOverflowed()) {
+ console.warn('Bindery: Page overflowing', book.pageInProgress.element);
+ if (!page.suppressErrors && !ignoreOverflow) {
+ paginateReject('Moved to new page when last one is still overflowing');
+ throw Error('Bindery: Moved to new page when last one is still overflowing');
+ }
+ }
+
+ // finished with this page, can display
+ book.pages = orderPages(book.pages, makeNewPage);
+ annotatePages(book.pages);
+ if (page) ruleSet.finishPage(page, book);
+ };
+
+ // Creates clones for ever level of tag
+ // we were in when we overflowed the last page
+ var continueOnNewPage = function continueOnNewPage() {
+ var ignoreOverflow = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
+
+ if (book.pages.length > MAXIMUM_PAGE_LIMIT) {
+ paginateReject('Maximum page count exceeded');
+ throw Error('Bindery: Maximum page count exceeded. Suspected runaway layout.');
+ }
+
+ finishPage(book.pageInProgress, ignoreOverflow);
+
+ breadcrumb = breadcrumbClone(breadcrumb, rules);
+ var newPage = makeNewPage();
+
+ book.pageInProgress = newPage;
+ progress(book);
+
+ book.pages.push(newPage);
+
+ if (breadcrumb[0]) {
+ newPage.flowContent.appendChild(breadcrumb[0]);
+ }
+
+ // make sure the cloned page is valid.
+ if (newPage.hasOverflowed()) {
+ var suspect = last(breadcrumb);
+ if (suspect) {
+ console.warn('Bindery: Content overflows, probably due to a style set on ' + elementToString(suspect) + '.');
+ suspect.parentNode.removeChild(suspect);
+ } else {
+ console.warn('Bindery: Content overflows.');
+ }
+ }
+
+ return newPage;
+ };
+
+ // Shifts this element to the next page. If any of its
+ // ancestors cannot be split across page, it will
+ // step up the tree to find the first ancestor
+ // that can be split, and move all of that descendants
+ // to the next page.
+ var moveElementToNextPage = function moveElementToNextPage(nodeToMove) {
+ // So this node won't get cloned. TODO: this is unclear
+ breadcrumb.pop();
+
+ if (breadcrumb.length < 1) {
+ throw Error('Bindery: Attempting to move the top-level element');
+ }
+
+ // find the nearest splittable parent
+ var willMove = nodeToMove;
+ var pathToRestore = [];
+ while (breadcrumb.length > 1 && !isSplittable(last(breadcrumb), ruleSet.selectorsNotToSplit)) {
+ // console.log('Not OK to split:', last(breadcrumb));
+ willMove = breadcrumb.pop();
+ pathToRestore.unshift(willMove);
+ }
+
+ // Once a node is moved to a new page, it should no longer trigger another
+ // move. otherwise tall elements will endlessly get shifted to the next page
+ willMove.setAttribute('data-bindery-did-move', true);
+
+ var parent = willMove.parentNode;
+ parent.removeChild(willMove);
+
+ if (breadcrumb.length > 1 && last(breadcrumb).textContent.trim() === '') {
+ parent.appendChild(willMove);
+ willMove = breadcrumb.pop();
+ pathToRestore.unshift(willMove);
+ willMove.parentNode.removeChild(willMove);
+ }
+
+ // If the page is empty when this node is removed,
+ // then it won't help to move it to the next page.
+ // Instead continue here until the node is done.
+ if (!book.pageInProgress.isEmpty) {
+ if (book.pageInProgress.hasOverflowed()) {
+ book.pageInProgress.suppressErrors = true;
+ }
+ continueOnNewPage();
+ }
+
+ // append node as first in new page
+ last(breadcrumb).appendChild(willMove);
+
+ // restore subpath
+ pathToRestore.forEach(function (restore) {
+ breadcrumb.push(restore);
+ });
+
+ breadcrumb.push(nodeToMove);
+ };
+
+ var addTextWithoutChecks = function addTextWithoutChecks(child, parent) {
+ return new Thenable(function (resolve) {
+ parent.appendChild(child);
+ if (canSplit()) {
+ book.pageInProgress.suppressErrors = true;
+ continueOnNewPage();
+ }
+ resolve();
+ });
+ };
+
+ var addSplittableText = function addSplittableText(text) {
+ return new Thenable(function (resolve, reject) {
+ addTextNodeIncremental(text, last(breadcrumb), book.pageInProgress).then(function (remainder) {
+ if (remainder) {
+ continueOnNewPage();
+ addSplittableText(remainder).then(resolve).catch(reject);
+ } else {
+ resolve();
+ }
+ }).catch(reject);
+ });
+ };
+
+ var canSplitParent = function canSplitParent(parent) {
+ return isSplittable(parent, ruleSet.selectorsNotToSplit) && !shouldIgnoreOverflow(parent);
+ };
+
+ var addTextChild = function addTextChild(child, parent) {
+ return (canSplitParent(parent) ? addSplittableText(child).catch(function () {
+ if (breadcrumb.length < 2) return addTextWithoutChecks(child, last(breadcrumb));
+ moveElementToNextPage(parent);
+ return addSplittableText(child);
+ }) : addTextNode(child, last(breadcrumb), book.pageInProgress).catch(function () {
+ if (canSplit()) moveElementToNextPage(parent);
+ return addTextNode(child, last(breadcrumb), book.pageInProgress);
+ })).catch(function () {
+ return addTextWithoutChecks(child, last(breadcrumb));
+ });
+ };
+
+ var addElementNode = void 0;
+
+ var addChild = function addChild(child, parent) {
+ if (isTextNode(child)) {
+ return addTextChild(child, parent);
+ } else if (isUnloadedImage(child)) {
+ var imgStart = performance.now();
+ return waitForImage(child).then(function () {
+ layoutWaitingTime += performance.now() - imgStart;
+ return addElementNode(child);
+ });
+ } else if (isContent(child)) {
+ return addElementNode(child);
+ }
+
+ // Skip comments and unknown nodes
+ return Thenable.resolved();
+ };
+
+ // Adds an element node by clearing its childNodes, then inserting them
+ // one by one recursively until thet overflow the page
+ addElementNode = function addElementNode(elementToAdd) {
+ return new Thenable(function (resolve) {
+ if (book.pageInProgress.hasOverflowed() && canSplit()) {
+ book.pageInProgress.suppressErrors = true;
+ continueOnNewPage();
+ }
+ var element = ruleSet.beforeAddElement(elementToAdd, book, continueOnNewPage, makeNewPage);
+
+ if (!breadcrumb[0]) book.pageInProgress.flowContent.appendChild(element);else last(breadcrumb).appendChild(element);
+
+ breadcrumb.push(element);
+
+ var childNodes = [].concat(toConsumableArray(element.childNodes));
+ element.innerHTML = '';
+
+ // Overflows when empty
+ if (book.pageInProgress.hasOverflowed() && canSplit()) {
+ moveElementToNextPage(element);
+ }
+
+ var finishAdding = function finishAdding() {
+ // We're now done with this element and its children,
+ // so we pop up a level
+ var addedChild = breadcrumb.pop();
+ ruleSet.afterAddElement(addedChild, book, continueOnNewPage, makeNewPage, function (el) {
+ el.parentNode.removeChild(el);
+ continueOnNewPage();
+ last(breadcrumb).appendChild(el);
+ });
+ elementsProcessed += 1;
+ book.estimatedProgress = elementsProcessed / elementCount;
+ resolve();
+ };
+
+ var index = 0;
+ var addNext = function addNext() {
+ if (index < childNodes.length) {
+ var child = childNodes[index];
+ index += 1;
+ addChild(child, element).then(addNext);
+ } else {
+ finishAdding();
+ }
+ };
+
+ // kick it off
+ addNext();
+ });
+ };
+
+ (function () {
+ var startLayoutTime = window.performance.now();
+
+ ruleSet.setup();
+ content.style.margin = 0;
+ content.style.padding = 0;
+ elementCount = content.querySelectorAll('*').length;
+ continueOnNewPage();
+ addElementNode(content).then(function () {
+ document.body.removeChild(measureArea);
+
+ book.pages = orderPages(book.pages, makeNewPage);
+ annotatePages(book.pages);
+
+ book.setCompleted();
+ ruleSet.finishEveryPage(book);
+
+ var endLayoutTime = window.performance.now();
+ var totalTime = endLayoutTime - startLayoutTime;
+ var layoutTime = totalTime - layoutWaitingTime;
+
+ console.log('\uD83D\uDCD6 Book ready in ' + sec(totalTime) + 's (Layout: ' + sec(layoutTime) + 's, Waiting for images: ' + sec(layoutWaitingTime) + 's)');
+
+ paginateResolve(book);
+ });
+ })();
+ });
+};
+
+var Mode = Object.freeze({
+ FLIPBOOK: 'view_flipbook',
+ PREVIEW: 'view_preview',
+ PRINT: 'view_print'
+});
+
+var Paper = Object.freeze({
+ AUTO: 'paper_auto',
+ AUTO_BLEED: 'paper_auto_bleed',
+ AUTO_MARKS: 'paper_auto_marks',
+ LETTER_PORTRAIT: 'paper_letter_p',
+ LETTER_LANDSCAPE: 'paper_letter_l',
+ A4_PORTRAIT: 'paper_a4_p',
+ A4_LANDSCAPE: 'paper_a4_l'
+});
+
+var Layout = Object.freeze({
+ PAGES: 'layout_pages',
+ SPREADS: 'layout_spreads',
+ BOOKLET: 'layout_booklet'
+});
+
+var Marks = Object.freeze({
+ NONE: 'marks_none',
+ SPREADS: 'layout_spreads',
+ BOTH: 'marks_both'
+});
+
+var letter = { width: '8.5in', height: '11in' };
+var a4 = { width: '210mm', height: '297mm' };
+var defaultPageSetup = {
+ bleed: '12pt',
+ size: { width: '4in', height: '6in' },
+ margin: {
+ inner: '24pt',
+ outer: '24pt',
+ bottom: '40pt',
+ top: '48pt'
+ }
+};
+
+var supportsCustomPageSize = !!window.chrome && !!window.chrome.webstore;
+
+var PageSetup = function () {
+ function PageSetup() {
+ var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ classCallCheck(this, PageSetup);
+
+ this.setSize(opts.size || defaultPageSetup.size);
+ this.setMargin(opts.margin || defaultPageSetup.margin);
+ this.setBleed(opts.bleed || defaultPageSetup.bleed);
+ }
+
+ createClass(PageSetup, [{
+ key: 'setupPaper',
+ value: function setupPaper() {
+ var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+
+ this.sheetSizeMode = supportsCustomPageSize ? opts.paper || Paper.AUTO : Paper.AUTO_MARKS;
+ this.printTwoUp = opts.layout && opts.layout !== Layout.PAGES;
+ }
+ }, {
+ key: 'setSize',
+ value: function setSize(size) {
+ isValidSize(size);
+ this.size = size;
+ }
+ }, {
+ key: 'setMargin',
+ value: function setMargin(margin) {
+ isValidSize(margin);
+ this.margin = margin;
+ }
+ }, {
+ key: 'setBleed',
+ value: function setBleed(newBleed) {
+ this.bleed = newBleed;
+ }
+ }, {
+ key: 'setPrintTwoUp',
+ value: function setPrintTwoUp(newVal) {
+ this.printTwoUp = newVal;
+ }
+ }, {
+ key: 'isSizeValid',
+ value: function isSizeValid() {
+ this.updateStylesheet();
+ return Page.isSizeValid();
+ }
+ }, {
+ key: 'spreadSizeStyle',
+ value: function spreadSizeStyle() {
+ var w = parseVal(this.size.width);
+ return {
+ height: this.size.height,
+ width: '' + w.val * 2 + w.unit
+ };
+ }
+ }, {
+ key: 'updateStylesheet',
+ value: function updateStylesheet() {
+ var sheet = void 0;
+ var existing = document.querySelector('#binderyPageSetup');
+ if (existing) {
+ sheet = existing;
+ } else {
+ sheet = document.createElement('style');
+ sheet.id = 'binderyPageSetup';
+ }
+ var w = parseVal(this.size.width);
+
+ sheet.innerHTML = '\n@page { size: ' + this.sheetSize.width + ' ' + this.sheetSize.height + '; }\n' + c('.print-page') + ' { width: ' + this.sheetSize.width + '; height: ' + this.sheetSize.height + ';}\n\n' + c('.show-crop') + ' ' + c('.print-page') + ' ' + c('.spread-wrapper') + ',\n' + c('.show-bleed-marks') + ' ' + c('.print-page') + ' ' + c('.spread-wrapper') + ' {\n margin: calc(' + this.bleed + ' + 12pt) auto;\n}\nhtml {\n --bindery-page-width: ' + this.size.width + ';\n --bindery-page-height: ' + this.size.height + ';\n}\n' + c('.page-size-rotated') + ' {\n height: ' + this.size.width + ';\n width: ' + this.size.height + ';\n}\n' + c('.spread-size') + ' {\n height: ' + this.size.height + ';\n width: ' + w.val * 2 + w.unit + ';\n}\n' + c('.spread-size-rotated') + ' {\n height: ' + w.val * 2 + w.unit + ';\n width: ' + this.size.height + ';\n}\n' + c('.flowbox') + ',\n' + c('.footer') + ' {\n margin-left: ' + this.margin.inner + ';\n margin-right: ' + this.margin.outer + ';\n}\n' + c('.left') + ' ' + c('.flowbox') + ',\n' + c('.left') + ' ' + c('.footer') + ' {\n margin-left: ' + this.margin.outer + ';\n margin-right: ' + this.margin.inner + ';\n}\n\n' + c('.left') + ' ' + c('.running-header') + ' {\n left: ' + this.margin.outer + ';\n}\n' + c('.right') + ' ' + c('.running-header') + ' {\n right: ' + this.margin.outer + ';\n}\n\n' + c('.flowbox') + ' { margin-top: ' + this.margin.top + '; }\n' + c('.footer') + '{ margin-bottom: ' + this.margin.bottom + '; }\n\n' + c('.bleed-left') + ',\n' + c('.bleed-right') + ',\n' + c('.crop-left') + ',\n' + c('.crop-right') + ',\n' + c('.crop-fold') + ' {\n top: calc( -12pt - ' + this.bleed + ' );\n bottom: calc( -12pt - ' + this.bleed + ' );\n}\n\n' + c('.bleed-top') + ',\n' + c('.bleed-bottom') + ',\n' + c('.crop-top') + ',\n' + c('.crop-bottom') + ' {\n left: calc( -12pt - ' + this.bleed + ' );\n right: calc( -12pt - ' + this.bleed + ' );\n}\n' + c('.bleed-left') + ' { left: -' + this.bleed + '; }\n' + c('.bleed-right') + ' { right: -' + this.bleed + '; }\n' + c('.bleed-top') + ' { top: -' + this.bleed + '; }\n' + c('.bleed-bottom') + ' { bottom: -' + this.bleed + '; }\n\n' + c('.background') + ' {\n top: -' + this.bleed + ';\n bottom: -' + this.bleed + ';\n left: -' + this.bleed + ';\n right: -' + this.bleed + ';\n}\n\n' + c('.spread') + c('.right') + ' > ' + c('.background') + ' {\n left: calc(-100% - ' + this.bleed + ');\n}\n' + c('.spread') + c('.left') + ' > ' + c('.background') + ' {\n right: calc(-100% - ' + this.bleed + ');\n}\n ';
+ document.head.appendChild(sheet);
+ }
+ }, {
+ key: 'displaySize',
+ get: function get$$1() {
+ var width = this.printTwoUp ? this.spreadSizeStyle().width : this.size.width;
+ var height = this.size.height;
+ var bleed = this.bleed;
+
+ return {
+ width: width,
+ height: height,
+ bleed: bleed
+ };
+ }
+ }, {
+ key: 'sheetSize',
+ get: function get$$1() {
+ var width = this.printTwoUp ? this.spreadSizeStyle().width : this.size.width;
+ var height = this.size.height;
+ var b = this.bleed;
+
+ switch (this.sheetSizeMode) {
+ case Paper.AUTO:
+ return { width: width, height: height };
+ case Paper.AUTO_BLEED:
+ return {
+ width: 'calc(' + width + ' + ' + b + ' + ' + b + ')',
+ height: 'calc(' + height + ' + ' + b + ' + ' + b + ')'
+ };
+ case Paper.AUTO_MARKS:
+ // TODO: 24pt marks is hardcoded
+ return {
+ width: 'calc(' + width + ' + ' + b + ' + ' + b + ' + 24pt)',
+ height: 'calc(' + height + ' + ' + b + ' + ' + b + ' + 24pt)'
+ };
+ case Paper.LETTER_LANDSCAPE:
+ return { width: letter.height, height: letter.width };
+ case Paper.LETTER_PORTRAIT:
+ return letter;
+ case Paper.A4_PORTRAIT:
+ return a4;
+ case Paper.A4_LANDSCAPE:
+ return { width: a4.height, height: a4.width };
+ default:
+ }
+ return { width: width, height: height };
+ }
+ }]);
+ return PageSetup;
+}();
+
+/* global BINDERY_VERSION */
+
+var errorView = function (title, text) {
+ return el('.error', [el('.error-title', title), el('.error-text', text), el('.error-footer', 'Bindery ' + BINDERY_VERSION)]);
+};
+
+var orderPagesBooklet = function orderPagesBooklet(pages, makePage) {
+ while (pages.length % 4 !== 0) {
+ var spacerPage = makePage();
+ spacerPage.element.style.visibility = 'hidden';
+ pages.push(spacerPage);
+ }
+ var bookletOrder = [];
+ var len = pages.length;
+
+ for (var i = 0; i < len / 2; i += 2) {
+ bookletOrder.push(pages[len - 1 - i]);
+ bookletOrder.push(pages[i]);
+ bookletOrder.push(pages[i + 1]);
+ bookletOrder.push(pages[len - 2 - i]);
+ }
+
+ return bookletOrder;
+};
+
+var padPages = function padPages(pages, makePage) {
+ if (pages.length % 2 !== 0) {
+ var pg = makePage();
+ pages.push(pg);
+ }
+ var spacerPage = makePage();
+ var spacerPage2 = makePage();
+ spacerPage.element.style.visibility = 'hidden';
+ spacerPage2.element.style.visibility = 'hidden';
+ pages.unshift(spacerPage);
+ pages.push(spacerPage2);
+
+ return pages;
+};
+
+var twoPageSpread = function twoPageSpread(children) {
+ return el('.spread-wrapper.spread-size', children);
+};
+var onePageSpread = function onePageSpread(children) {
+ return el('.spread-wrapper.page-size', children);
+};
+
+var renderGridLayout = function renderGridLayout(pages, isTwoUp) {
+ var gridLayout = document.createDocumentFragment();
+ if (isTwoUp) {
+ for (var i = 0; i < pages.length; i += 2) {
+ var wrap = twoPageSpread([pages[i].element, pages[i + 1].element]);
+ gridLayout.appendChild(wrap);
+ }
+ } else {
+ pages.forEach(function (pg) {
+ var wrap = onePageSpread([pg.element]);
+ gridLayout.appendChild(wrap);
+ });
+ }
+
+ return gridLayout;
+};
+
+var directions = ['top', 'bottom', 'left', 'right'];
+var bleedMarks = function bleedMarks() {
+ return directions.map(function (dir) {
+ return el('.bleed-' + dir);
+ });
+};
+var cropMarks = function cropMarks() {
+ return directions.map(function (dir) {
+ return el('.crop' + dir);
+ });
+};
+
+var printMarksSingle = function printMarksSingle() {
+ return el('.print-mark-wrap', [].concat(toConsumableArray(cropMarks()), toConsumableArray(bleedMarks())));
+};
+
+var printMarksSpread = function printMarksSpread() {
+ return el('.print-mark-wrap', [el('.crop-fold')].concat(toConsumableArray(cropMarks()), toConsumableArray(bleedMarks())));
+};
+
+var bookletMeta = function bookletMeta(i, len) {
+ var isFront = i % 4 === 0;
+ var sheetIndex = parseInt((i + 1) / 4, 10) + 1;
+ return el('.print-meta', 'Sheet ' + sheetIndex + ' of ' + len / 4 + ': ' + (isFront ? 'Outside' : 'Inside'));
+};
+
+var twoPageSpread$1 = function twoPageSpread(children) {
+ return el('.spread-wrapper.spread-size', children);
+};
+var onePageSpread$1 = function onePageSpread(children) {
+ return el('.spread-wrapper.page-size', children);
+};
+
+var renderPrintLayout = function renderPrintLayout(pages, isTwoUp, isBooklet) {
+ var printLayout = document.createDocumentFragment();
+
+ var marks = isTwoUp ? printMarksSpread : printMarksSingle;
+ var spread = isTwoUp ? twoPageSpread$1 : onePageSpread$1;
+
+ var printSheet = function printSheet(children) {
+ return el('.print-page', [spread(children)]);
+ };
+
+ if (isTwoUp) {
+ for (var i = 0; i < pages.length; i += 2) {
+ var spreadMarks = marks();
+ if (isBooklet) {
+ var meta = bookletMeta(i, pages.length);
+ spreadMarks.appendChild(meta);
+ }
+ var sheet = printSheet([pages[i].element, pages[i + 1].element, spreadMarks]);
+ printLayout.appendChild(sheet);
+ }
+ } else {
+ pages.forEach(function (pg) {
+ var sheet = printSheet([pg.element, marks()]);
+ printLayout.appendChild(sheet);
+ });
+ }
+
+ return printLayout;
+};
+
+var renderFlipLayout = function renderFlipLayout(pages, doubleSided) {
+ var flipLayout = document.createDocumentFragment();
+ var sizer = el('.spread-size.flip-sizer');
+ var flapHolder = el('.spread-size.flap-holder');
+ sizer.appendChild(flapHolder);
+ flipLayout.appendChild(sizer);
+ var flaps = [];
+ var currentLeaf = -1;
+
+ var leftOffset = 4;
+ if (pages.length * leftOffset > 60) {
+ leftOffset = 60 / pages.length;
+ }
+ flapHolder.style.width = pages.length * leftOffset + 'px';
+
+ var setLeaf = function setLeaf(n) {
+ var newLeaf = n;
+ if (newLeaf === currentLeaf) newLeaf += 1;
+ currentLeaf = newLeaf;
+
+ var zScale = 4;
+ if (flaps.length * zScale > 200) zScale = 200 / flaps.length;
+
+ flaps.forEach(function (flap, i, arr) {
+ // + 0.5 so left and right are even
+ var z = (arr.length - Math.abs(i - newLeaf + 0.5)) * zScale;
+ flap.style.transform = 'translate3d(' + (i < newLeaf ? 4 : 0) + 'px,0,' + z + 'px) rotateY(' + (i < newLeaf ? -180 : 0) + 'deg)';
+ });
+ };
+
+ var leafIndex = 0;
+
+ var _loop = function _loop(i) {
+ leafIndex += 1;
+ var li = leafIndex;
+ var flap = el('.page3d');
+ flap.addEventListener('click', function () {
+ var newLeaf = li - 1;
+ setLeaf(newLeaf);
+ });
+
+ var rightPage = pages[i].element;
+ var leftPage = void 0;
+ rightPage.classList.add(c('page3d-front'));
+ flap.appendChild(rightPage);
+ if (doubleSided) {
+ flap.classList.add(c('doubleSided'));
+ leftPage = pages[i + 1].element;
+ leftPage.classList.add(c('page3d-back'));
+ flap.appendChild(leftPage);
+ } else {
+ leftPage = el('.page.page3d-back');
+ flap.appendChild(leftPage);
+ }
+ // TODO: Dynamically add/remove pages.
+ // Putting 1000s of elements onscreen
+ // locks up the browser.
+
+ flap.style.left = i * leftOffset + 'px';
+
+ flaps.push(flap);
+ flapHolder.appendChild(flap);
+ };
+
+ for (var i = 1; i < pages.length - 1; i += doubleSided ? 2 : 1) {
+ _loop(i);
+ }
+
+ setLeaf(0);
+ return flipLayout;
+};
+
+// import Controls from './Controls';
+var modeAttr = {};
+modeAttr[Mode.PREVIEW] = 'preview';
+modeAttr[Mode.PRINT] = 'print';
+modeAttr[Mode.FLIPBOOK] = 'flip';
+
+var Viewer = function () {
+ function Viewer(_ref) {
+ var _this = this;
+
+ var bindery = _ref.bindery,
+ mode = _ref.mode,
+ layout = _ref.layout,
+ marks = _ref.marks,
+ ControlsComponent = _ref.ControlsComponent;
+ classCallCheck(this, Viewer);
+
+ this.book = null;
+ this.pageSetup = bindery.pageSetup;
+
+ this.progressBar = el('.progress-bar');
+ this.zoomBox = el('zoom-wrap');
+ this.element = el('root', [this.progressBar, this.zoomBox]);
+
+ this.doubleSided = true;
+ this.printArrange = layout;
+
+ this.setMarks(marks);
+ this.mode = mode;
+ this.element.setAttribute('bindery-view-mode', modeAttr[this.mode]);
+ this.currentLeaf = 0;
+
+ this.listenForPrint();
+ this.listenForResize();
+
+ this.setPrint = this.setPrint.bind(this);
+
+ if (ControlsComponent) {
+ this.controls = new ControlsComponent({ Mode: Mode, Paper: Paper, Layout: Layout, Marks: Marks }, // Available options
+ { // Initial props
+ paper: this.pageSetup.sheetSizeMode,
+ layout: this.printArrange,
+ mode: this.mode,
+ marks: marks
+ }, { // Actions
+ setMode: function setMode(newMode) {
+ if (newMode === _this.mode) return;
+ _this.mode = newMode;
+ _this.render();
+ },
+ setPaper: this.setSheetSize.bind(this),
+ setLayout: this.setPrintArrange.bind(this),
+ setMarks: this.setMarks.bind(this),
+ getPageSize: function getPageSize() {
+ return _this.pageSetup.displaySize;
+ }
+ });
+ this.element.appendChild(this.controls.element);
+ }
+
+ this.element.classList.add(c('in-progress'));
+
+ document.body.appendChild(this.element);
+ }
+
+ // Automatically switch into print mode
+
+
+ createClass(Viewer, [{
+ key: 'listenForPrint',
+ value: function listenForPrint() {
+ var _this2 = this;
+
+ if (window.matchMedia) {
+ var mediaQueryList = window.matchMedia('print');
+ mediaQueryList.addListener(function (mql) {
+ if (mql.matches) {
+ // before print
+ _this2.setPrint();
+ } else {
+ // after print
+ }
+ });
+ }
+ document.body.addEventListener('keydown', function (e) {
+ if ((e.ctrlKey || e.metaKey) && e.keyCode === 80) {
+ e.preventDefault();
+ _this2.setPrint();
+ setTimeout(function () {
+ window.print();
+ }, 10);
+ }
+ });
+ }
+ }, {
+ key: 'listenForResize',
+ value: function listenForResize() {
+ var _this3 = this;
+
+ window.addEventListener('resize', function () {
+ if (!_this3.throttleResize) {
+ _this3.updateZoom();
+ _this3.throttleResize = setTimeout(function () {
+ _this3.throttleResize = null;
+ }, 20);
+ }
+ });
+ }
+ }, {
+ key: 'setInProgress',
+ value: function setInProgress() {
+ this.element.classList.add(c('in-progress'));
+ if (this.controls) this.controls.setInProgress();
+ }
+ }, {
+ key: 'setSheetSize',
+ value: function setSheetSize(newVal) {
+ var _this4 = this;
+
+ this.pageSetup.sheetSizeMode = newVal;
+ this.pageSetup.updateStylesheet();
+
+ if (this.mode !== Mode.PRINT) {
+ this.setPrint();
+ }
+ this.updateZoom();
+ setTimeout(function () {
+ _this4.updateZoom();
+ }, 300);
+ }
+ }, {
+ key: 'setPrintArrange',
+ value: function setPrintArrange(newVal) {
+ if (newVal === this.printArrange) return;
+ this.printArrange = newVal;
+
+ this.pageSetup.setPrintTwoUp(this.isTwoUp);
+ this.pageSetup.updateStylesheet();
+
+ if (this.mode === Mode.PRINT) {
+ this.render();
+ } else {
+ this.setPrint();
+ }
+ }
+ }, {
+ key: 'setMarks',
+ value: function setMarks(newVal) {
+ switch (newVal) {
+ case Marks.NONE:
+ this.isShowingCropMarks = false;
+ this.isShowingBleedMarks = false;
+ break;
+ case Marks.CROP:
+ this.isShowingCropMarks = true;
+ this.isShowingBleedMarks = false;
+ break;
+ case Marks.BLEED:
+ this.isShowingCropMarks = false;
+ this.isShowingBleedMarks = true;
+ break;
+ case Marks.BOTH:
+ this.isShowingCropMarks = true;
+ this.isShowingBleedMarks = true;
+ break;
+ default:
+ }
+ }
+ }, {
+ key: 'displayError',
+ value: function displayError(title, text) {
+ if (!this.element.parentNode) {
+ document.body.appendChild(this.element);
+ }
+ if (!this.error) {
+ this.error = errorView(title, text);
+ this.element.appendChild(this.error);
+ }
+ }
+ }, {
+ key: 'clear',
+ value: function clear() {
+ this.book = null;
+ this.lastSpreadInProgress = null; // TODO: Make this clearer, after first render
+ this.zoomBox.innerHTML = '';
+ }
+ }, {
+ key: 'cancel',
+ value: function cancel() {
+ // TODO this doesn't work if the target is an existing node
+ if (this.element.parentNode) {
+ this.element.parentNode.removeChild(this.element);
+ }
+ }
+ }, {
+ key: 'toggleBleed',
+ value: function toggleBleed() {
+ this.element.classList.add(c('show-bleed'));
+ }
+ }, {
+ key: 'toggleDouble',
+ value: function toggleDouble() {
+ this.doubleSided = !this.doubleSided;
+ this.render();
+ }
+ }, {
+ key: 'setPrint',
+ value: function setPrint() {
+ if (this.mode === Mode.PRINT) return;
+ this.mode = Mode.PRINT;
+ this.render();
+ }
+ }, {
+ key: 'render',
+ value: function render() {
+ var _this5 = this;
+
+ if (!this.book) return;
+ var _document = document,
+ body = _document.body;
+
+ if (!this.element.parentNode) {
+ body.appendChild(this.element);
+ }
+
+ this.flaps = [];
+ body.classList.add(c('viewing'));
+ this.element.setAttribute('bindery-view-mode', modeAttr[this.mode]);
+
+ var scrollMax = body.scrollHeight - body.offsetHeight;
+ var scrollPct = body.scrollTop / scrollMax;
+
+ if (this.controls) this.controls.setDone(this.book.pages.length);
+ this.progressBar.style.width = '100%';
+
+ window.requestAnimationFrame(function () {
+ if (_this5.mode === Mode.PREVIEW) _this5.renderGrid();else if (_this5.mode === Mode.FLIPBOOK) _this5.renderInteractive();else if (_this5.mode === Mode.PRINT) _this5.renderPrint();else _this5.renderGrid();
+
+ body.scrollTop = scrollMax * scrollPct;
+ _this5.updateZoom();
+ });
+ }
+ }, {
+ key: 'renderProgress',
+ value: function renderProgress(book) {
+ var _this6 = this;
+
+ this.book = book;
+
+ this.progressBar.style.width = this.book.estimatedProgress * 100 + '%';
+
+ if (this.controls) {
+ this.controls.updateProgress(this.book.pages.length, this.book.estimatedProgress);
+ }
+
+ var sideBySide = this.mode === Mode.PREVIEW || this.mode === Mode.PRINT && this.printArrange !== Layout.PAGES;
+ var limit = sideBySide ? 2 : 1;
+
+ var makeSpread = function makeSpread() {
+ for (var _len = arguments.length, arg = Array(_len), _key = 0; _key < _len; _key++) {
+ arg[_key] = arguments[_key];
+ }
+
+ return el('.spread-wrapper', [].concat(arg));
+ };
+
+ this.book.pages.forEach(function (page, i) {
+ // If hasn't been added, or not in spread yet
+ if (!_this6.zoomBox.contains(page.element) || page.element.parentNode === _this6.zoomBox) {
+ if (_this6.lastSpreadInProgress && _this6.lastSpreadInProgress.children.length < limit) {
+ _this6.lastSpreadInProgress.appendChild(page.element);
+ } else {
+ if (i === 0 && sideBySide) {
+ var spacer = new Page();
+ spacer.element.style.visibility = 'hidden';
+ _this6.lastSpreadInProgress = makeSpread(spacer.element, page.element);
+ } else {
+ _this6.lastSpreadInProgress = makeSpread(page.element);
+ }
+ _this6.zoomBox.appendChild(_this6.lastSpreadInProgress);
+ }
+ }
+ });
+
+ if (this.book.pageInProgress) {
+ this.zoomBox.appendChild(this.book.pageInProgress.element);
+ }
+
+ this.updateZoom();
+ }
+ }, {
+ key: 'updateZoom',
+ value: function updateZoom() {
+ if (this.zoomBox.firstElementChild) {
+ var scrollPct = document.body.scrollTop / document.body.scrollHeight;
+ var viewerRect = this.zoomBox.getBoundingClientRect();
+ var contentW = this.zoomBox.firstElementChild.getBoundingClientRect().width;
+ var scale = Math.min(1, viewerRect.width / contentW);
+
+ this.zoomBox.style.transform = 'scale(' + scale + ')';
+ document.body.scrollTop = document.body.scrollHeight * scrollPct;
+ }
+ }
+ }, {
+ key: 'renderPrint',
+ value: function renderPrint() {
+ this.element.classList.add(c('show-bleed'));
+
+ this.zoomBox.innerHTML = '';
+
+ var isBooklet = this.printArrange === Layout.BOOKLET;
+
+ var pages = this.book.pages.slice();
+ if (this.printArrange === Layout.SPREADS) {
+ pages = padPages(pages, function () {
+ return new Page();
+ });
+ } else if (isBooklet) {
+ pages = orderPagesBooklet(pages, function () {
+ return new Page();
+ });
+ }
+
+ var fragment = renderPrintLayout(pages, this.isTwoUp, isBooklet);
+ this.zoomBox.appendChild(fragment);
+ }
+ }, {
+ key: 'renderGrid',
+ value: function renderGrid() {
+ this.zoomBox.innerHTML = '';
+
+ this.element.classList.remove(c('show-bleed'));
+
+ var pages = this.book.pages.slice();
+
+ if (this.doubleSided) pages = padPages(pages, function () {
+ return new Page();
+ });
+
+ var fragment = renderGridLayout(pages, this.doubleSided);
+ this.zoomBox.appendChild(fragment);
+ }
+ }, {
+ key: 'renderInteractive',
+ value: function renderInteractive() {
+ this.zoomBox.innerHTML = '';
+ this.flaps = [];
+
+ this.element.classList.remove(c('show-bleed'));
+
+ var pages = padPages(this.book.pages.slice(), function () {
+ return new Page();
+ });
+
+ var fragment = renderFlipLayout(pages, this.doubleSided);
+ this.zoomBox.appendChild(fragment);
+ }
+ }, {
+ key: 'isTwoUp',
+ get: function get$$1() {
+ return this.printArrange !== Layout.PAGES;
+ }
+ }, {
+ key: 'isShowingCropMarks',
+ get: function get$$1() {
+ return this.element.classList.contains(c('show-crop'));
+ },
+ set: function set$$1(newVal) {
+ if (newVal) {
+ this.element.classList.add(c('show-crop'));
+ this.setPrint();
+ } else {
+ this.element.classList.remove(c('show-crop'));
+ }
+ }
+ }, {
+ key: 'isShowingBleedMarks',
+ get: function get$$1() {
+ return this.element.classList.contains(c('show-bleed-marks'));
+ },
+ set: function set$$1(newVal) {
+ if (newVal) {
+ this.element.classList.add(c('show-bleed-marks'));
+ this.setPrint();
+ } else {
+ this.element.classList.remove(c('show-bleed-marks'));
+ }
+ }
+ }]);
+ return Viewer;
+}();
+
+var Split = function (_Rule) {
+ inherits(Split, _Rule);
+
+ function Split(options) {
+ classCallCheck(this, Split);
+
+ options.toNext = options.toNext || 'split-to-next';
+ options.fromPrevious = options.fromPrevious || 'split-from-previous';
+
+ var _this = possibleConstructorReturn(this, (Split.__proto__ || Object.getPrototypeOf(Split)).call(this, options));
+
+ OptionType.validate(options, {
+ name: 'Split',
+ selector: OptionType.string,
+ toNext: OptionType.string,
+ fromPrevious: OptionType.string
+ });
+ return _this;
+ }
+
+ createClass(Split, [{
+ key: 'customToNextClass',
+ get: function get$$1() {
+ return this.toNext;
+ }
+ }, {
+ key: 'customFromPreviousClass',
+ get: function get$$1() {
+ return this.fromPrevious;
+ }
+ }]);
+ return Split;
+}(Rule);
+
+var Counter = function (_Rule) {
+ inherits(Counter, _Rule);
+
+ function Counter(options) {
+ classCallCheck(this, Counter);
+
+ var _this = possibleConstructorReturn(this, (Counter.__proto__ || Object.getPrototypeOf(Counter)).call(this, options));
+
+ _this.selector = '*';
+ _this.counterValue = 0;
+ OptionType.validate(options, {
+ name: 'Counter',
+ replaceEl: OptionType.string,
+ resetEl: OptionType.string,
+ incrementEl: OptionType.string,
+ replace: OptionType.func
+ });
+ return _this;
+ }
+
+ createClass(Counter, [{
+ key: 'setup',
+ value: function setup() {
+ this.counterValue = 0;
+ }
+ }, {
+ key: 'beforeAdd',
+ value: function beforeAdd(el$$1) {
+ if (el$$1.matches(this.incrementEl)) {
+ this.counterValue += 1;
+ }
+ if (el$$1.matches(this.resetEl)) {
+ this.counterValue = 0;
+ }
+ if (el$$1.matches(this.replaceEl)) {
+ return this.createReplacement(el$$1);
+ }
+ return el$$1;
+ }
+ }, {
+ key: 'createReplacement',
+ value: function createReplacement(element) {
+ return this.replace(element, this.counterValue);
+ }
+ }, {
+ key: 'replace',
+ value: function replace(element, counterValue) {
+ element.textContent = counterValue;
+ return element;
+ }
+ }]);
+ return Counter;
+}(Rule);
+
+// Options:
+// selector: String
+// replace: function (HTMLElement) => HTMLElement
+
+var Replace = function (_Rule) {
+ inherits(Replace, _Rule);
+
+ function Replace(options) {
+ classCallCheck(this, Replace);
+
+ var _this = possibleConstructorReturn(this, (Replace.__proto__ || Object.getPrototypeOf(Replace)).call(this, options));
+
+ _this.name = 'Replace';
+ return _this;
+ }
+
+ createClass(Replace, [{
+ key: 'afterAdd',
+ value: function afterAdd(element, book, continueOnNewPage, makeNewPage, overflowCallback) {
+ var parent = element.parentNode;
+ if (!parent) {
+ throw Error('Bindery: Rule assumes element has been added but it has no parent.', element);
+ }
+ var defensiveClone = element.cloneNode(true);
+ var replacement = this.createReplacement(book, defensiveClone);
+ parent.replaceChild(replacement, element);
+
+ if (book.pageInProgress.hasOverflowed()) {
+ parent.replaceChild(element, replacement);
+
+ return overflowCallback(element);
+ }
+
+ return replacement;
+ }
+ }, {
+ key: 'createReplacement',
+ value: function createReplacement(book, element) {
+ return this.replace(element);
+ }
+ }, {
+ key: 'replace',
+ value: function replace(element) {
+ element.insertAdjacentHTML('beforeEnd', 'Default Replacement ');
+ return element;
+ }
+ }]);
+ return Replace;
+}(Rule);
+
+// Options:
+// selector: String
+// replace: function (HTMLElement, number) => HTMLElement
+// render: function (Page) => HTMLElement
+
+var Footnote = function (_Replace) {
+ inherits(Footnote, _Replace);
+
+ function Footnote(options) {
+ classCallCheck(this, Footnote);
+
+ var _this = possibleConstructorReturn(this, (Footnote.__proto__ || Object.getPrototypeOf(Footnote)).call(this, options));
+
+ OptionType.validate(options, {
+ name: 'Footnote',
+ selector: OptionType.string,
+ replace: OptionType.func,
+ render: OptionType.func
+ });
+ return _this;
+ }
+
+ createClass(Footnote, [{
+ key: 'afterAdd',
+ value: function afterAdd(element, book, continueOnNewPage, makeNewPage, overflowCallback) {
+ var number = book.pageInProgress.footer.children.length + 1;
+
+ var footnote = el('.footnote');
+ var contents = this.render(element, number);
+ if (contents instanceof HTMLElement) footnote.appendChild(contents);else footnote.innerHTML = contents;
+
+ book.pageInProgress.footer.appendChild(footnote);
+
+ return get(Footnote.prototype.__proto__ || Object.getPrototypeOf(Footnote.prototype), 'afterAdd', this).call(this, element, book, continueOnNewPage, makeNewPage, function (overflowEl) {
+ book.pageInProgress.footer.removeChild(footnote);
+ return overflowCallback(overflowEl);
+ });
+ }
+ }, {
+ key: 'createReplacement',
+ value: function createReplacement(book, element) {
+ var number = book.pageInProgress.footer.children.length;
+ return this.replace(element, number);
+ }
+ }, {
+ key: 'replace',
+ value: function replace(element, number) {
+ element.insertAdjacentHTML('beforeEnd', '' + number + ' ');
+ return element;
+ }
+ }, {
+ key: 'render',
+ value: function render(element, number) {
+ return '' + number + ' Default footnote (Learn how to change it )';
+ }
+ }]);
+ return Footnote;
+}(Replace);
+
+// Options:
+// selector: String
+// replace: function (HTMLElement, number) => HTMLElement
+
+var PageReference = function (_Replace) {
+ inherits(PageReference, _Replace);
+
+ function PageReference(options) {
+ classCallCheck(this, PageReference);
+
+ var _this = possibleConstructorReturn(this, (PageReference.__proto__ || Object.getPrototypeOf(PageReference)).call(this, options));
+
+ OptionType.validate(options, {
+ name: 'PageReference',
+ selector: OptionType.string,
+ replace: OptionType.func,
+ createTest: OptionType.func
+ });
+ return _this;
+ }
+
+ createClass(PageReference, [{
+ key: 'afterAdd',
+ value: function afterAdd(elmt, book) {
+ var _this2 = this;
+
+ var test = this.createTest(elmt);
+ if (test) {
+ // Temporary, to make sure it'll fit
+ var parent = elmt.parentNode;
+ var tempClone = elmt.cloneNode(true);
+ var tempNumbers = book.pagesForTest(test);
+ var tempRanges = makeRanges(tempNumbers);
+ var temp = this.replace(tempClone, tempRanges || '###');
+ temp.classList.add(c('placeholder-pulse'));
+ parent.replaceChild(temp, elmt);
+
+ book.onComplete(function () {
+ var tempParent = temp.parentNode;
+ var finalClone = elmt.cloneNode(true);
+ var pageNumbers = book.pagesForTest(test);
+ var pageRanges = makeRanges(pageNumbers);
+ var newEl = _this2.replace(finalClone, pageRanges);
+ tempParent.replaceChild(newEl, temp);
+ });
+
+ return temp;
+ }
+ return elmt;
+ }
+ }, {
+ key: 'createTest',
+ value: function createTest(element) {
+ var selector = element.getAttribute('href');
+ if (selector) {
+ selector = selector.replace('#', '');
+ // extra resilient in case it starts with a number ie wikipedia
+ selector = '[id="' + selector + '"]';
+ return function (el$$1) {
+ return el$$1.querySelector(selector);
+ };
+ }
+ return null;
+ }
+ }, {
+ key: 'replace',
+ value: function replace(original, number) {
+ original.insertAdjacentHTML('beforeend', ', ' + number + ' ');
+ return original;
+ }
+ }]);
+ return PageReference;
+}(Replace);
+
+// Options:
+// selector: String
+// render: function (Page) => HTMLElement
+// TODO selectorHierarchy: [ String ], ie [ 'h1', 'h2', 'h3.chapter' ]
+
+var RunningHeader = function (_Rule) {
+ inherits(RunningHeader, _Rule);
+
+ function RunningHeader() {
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ classCallCheck(this, RunningHeader);
+
+ var _this = possibleConstructorReturn(this, (RunningHeader.__proto__ || Object.getPrototypeOf(RunningHeader)).call(this, options));
+
+ OptionType.validate(options, {
+ name: 'RunningHeader',
+ render: OptionType.func
+ });
+ return _this;
+ }
+
+ createClass(RunningHeader, [{
+ key: 'eachPage',
+ value: function eachPage(page) {
+ if (!page.runningHeader) {
+ var elmt = el('.running-header');
+ page.element.appendChild(elmt);
+ page.runningHeader = elmt;
+ }
+ page.runningHeader.innerHTML = this.render(page);
+ }
+ }, {
+ key: 'render',
+ value: function render(page) {
+ return page.number;
+ }
+ }]);
+ return RunningHeader;
+}(Rule);
+
+var Rules = {
+ Rule: Rule,
+ Split: function Split$$1(options) {
+ return new Split(options);
+ },
+ Counter: function Counter$$1(options) {
+ return new Counter(options);
+ },
+ FullBleedPage: function FullBleedPage$$1(options) {
+ return new FullBleedPage(options);
+ },
+ Footnote: function Footnote$$1(options) {
+ return new Footnote(options);
+ },
+ RunningHeader: function RunningHeader$$1(options) {
+ return new RunningHeader(options);
+ },
+ Replace: function Replace$$1(options) {
+ return new Replace(options);
+ },
+ FullBleedSpread: function FullBleedSpread$$1(options) {
+ return new FullBleedSpread(options);
+ },
+ PageBreak: function PageBreak$$1(options) {
+ return new PageBreak(options);
+ },
+ PageReference: function PageReference$$1(options) {
+ return new PageReference(options);
+ },
+ createRule: function createRule(options) {
+ return new Rule(options);
+ }
+};
+
+var PageBreak$1 = Rules.PageBreak;
+var PageReference$1 = Rules.PageReference;
+var Footnote$1 = Rules.Footnote;
+var FullBleedPage$1 = Rules.FullBleedPage;
+var FullBleedSpread$1 = Rules.FullBleedSpread;
+
+
+var replacer = function replacer(element, number) {
+ element.textContent = '' + number;
+ return element;
+};
+
+var defaultRules = [PageBreak$1({ selector: '[book-page-break="both"]', position: 'both' }), PageBreak$1({ selector: '[book-page-break="avoid"]', position: 'avoid' }), PageBreak$1({ selector: '[book-page-break="after"][book-page-continue="right"]', position: 'after', continue: 'right' }), PageBreak$1({ selector: '[book-page-break="after"][book-page-continue="left"]', position: 'after', continue: 'left' }), PageBreak$1({ selector: '[book-page-break="after"][book-page-continue="next"]', position: 'after', continue: 'next' }), PageBreak$1({ selector: '[book-page-break="before"][book-page-continue="right"]', position: 'before', continue: 'right' }), PageBreak$1({ selector: '[book-page-break="before"][book-page-continue="left"]', position: 'before', continue: 'left' }), PageBreak$1({ selector: '[book-page-break="before"][book-page-continue="next"]', position: 'before', continue: 'next' }), FullBleedPage$1({ selector: '[book-full-bleed="page"]' }), FullBleedSpread$1({ selector: '[book-full-bleed="spread"]' }), Footnote$1({
+ selector: '[book-footnote-text]',
+ render: function render(element, number) {
+ var txt = element.getAttribute('book-footnote-text');
+ return '' + number + ' ' + txt;
+ }
+}), PageReference$1({
+ selector: '[book-pages-with-text]',
+ replace: replacer,
+ createTest: function createTest(element) {
+ var term = element.getAttribute('book-pages-with-text').toLowerCase().trim();
+ return function (page) {
+ var txt = page.textContent.toLowerCase();
+ return txt.includes(term);
+ };
+ }
+}), PageReference$1({
+ selector: '[book-pages-with-selector]',
+ replace: replacer,
+ createTest: function createTest(element) {
+ var sel = element.getAttribute('book-pages-with-selector').trim();
+ return function (page) {
+ return page.querySelector(sel);
+ };
+ }
+}), PageReference$1({
+ selector: '[book-pages-with]',
+ replace: replacer,
+ createTest: function createTest(element) {
+ var term = element.textContent.toLowerCase().trim();
+ return function (page) {
+ var txt = page.textContent.toLowerCase();
+ return txt.includes(term);
+ };
+ }
+})];
+
+___$insertStyle("@charset \"UTF-8\";@media screen{.📖-page{background:#fff;outline:1px solid #ddd;box-shadow:0 2px 4px -1px rgba(0,0,0,.15);overflow:hidden}.📖-show-bleed .📖-page{box-shadow:none;outline:none;overflow:visible}.📖-page:after{content:\"\";position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;z-index:3}}li.📖-continuation,p.📖-continuation{text-indent:unset!important}li.📖-continuation{list-style:none!important}.📖-out-of-flow{display:none}.📖-page{width:var(--bindery-page-width);height:var(--bindery-page-height);position:relative;display:flex;flex-direction:column;flex-wrap:nowrap}.📖-flowbox{position:relative;margin:60px 40px;margin-bottom:0;flex:1 1 auto;min-height:0}.📖-content{padding:.1px;position:relative}.📖-footer{margin:60px 40px;margin-top:8pt;flex:0 1 auto;z-index:1}.📖-background{position:absolute;z-index:0;overflow:hidden}.📖-left>.📖-background{right:0}.📖-right>.📖-background{left:0}.📖-sup{font-size:.667em}.📖-footer,.📖-running-header{font-size:10pt}.📖-running-header{position:absolute;text-align:center;top:.25in}.📖-left .📖-running-header{left:18pt;text-align:left}.📖-right .📖-running-header{right:18pt;text-align:right}.📖-left .📖-rotate-container.📖-rotate-outward,.📖-left .📖-rotate-container.📖-rotate-spread-clockwise,.📖-right .📖-rotate-container.📖-rotate-inward,.📖-rotate-container.📖-rotate-clockwise{transform:rotate(90deg) translate3d(0,-100%,0);transform-origin:top left}.📖-left .📖-rotate-container.📖-rotate-inward,.📖-left .📖-rotate-container.📖-rotate-spread-counterclockwise,.📖-right .📖-rotate-container.📖-rotate-outward,.📖-rotate-container.📖-rotate-counterclockwise{transform:rotate(-90deg) translate3d(-100%,0,0);transform-origin:top left}.📖-rotate-container{position:absolute}.📖-left .📖-rotate-container.📖-rotate-clockwise .📖-background{bottom:0}.📖-left .📖-rotate-container.📖-rotate-counterclockwise .📖-background,.📖-right .📖-rotate-container.📖-rotate-clockwise .📖-background{top:0}.📖-right .📖-rotate-container.📖-rotate-counterclockwise .📖-background,.📖-rotate-container.📖-rotate-inward .📖-background{bottom:0}.📖-rotate-container.📖-rotate-outward .📖-background{top:0}.📖-right .📖-rotate-container.📖-rotate-spread-clockwise{transform:rotate(90deg) translate3d(0,-50%,0);transform-origin:top left}.📖-right .📖-rotate-container.📖-rotate-spread-counterclockwise{transform:rotate(-90deg) translate3d(-100%,-50%,0);transform-origin:top left}@media screen{.📖-viewing{background:#f4f4f4!important}.📖-root{transition:opacity .2s;opacity:1;background:#f4f4f4;padding:10px;z-index:2;position:relative;padding-top:60px;min-height:90vh}.📖-progress-bar{position:fixed;left:0;top:0;background:var(--bindery-ui-accent,#0000c5);width:0;transition:all .2s;opacity:0;height:0;z-index:2}.📖-in-progress .📖-progress-bar{opacity:1;height:2px}.📖-measure-area{position:fixed;background:#f4f4f4;padding:50px 20px;z-index:2;visibility:hidden;left:0;right:0;bottom:0}.📖-measure-area .📖-page{margin:0 auto 50px}.📖-is-overflowing{border-bottom:1px solid #f0f}.📖-print-page{margin:0 auto}.📖-error{font:16px/1.4 -apple-system,BlinkMacSystemFont,Roboto,sans-serif;padding:15vh 15vw;z-index:3;position:fixed;top:0;left:0;right:0;bottom:0;background:hsla(0,0%,96%,.7)}.📖-error-title{font-size:1.5em;margin-bottom:16px}.📖-error-text{margin-bottom:16px;white-space:pre-line}.📖-error-footer{opacity:.5;font-size:.66em;text-transform:uppercase;letter-spacing:.02em}.📖-show-bleed .📖-print-page{background:#fff;outline:1px solid rgba(0,0,0,.1);box-shadow:0 1px 3px rgba(0,0,0,.2);margin:20px auto}.📖-placeholder-pulse{animation:pulse 1s infinite}}@keyframes pulse{0%{opacity:.2}50%{opacity:.5}to{opacity:.2}}@page{margin:0}@media print{.📖-root *{-webkit-print-color-adjust:exact;color-adjust:exact}.📖-controls,.📖-viewing>:not(.📖-root){display:none!important}.📖-print-page{padding:1px;margin:0 auto}.📖-zoom-wrap[style]{transform:none!important}}body.📖-viewing{margin:0}.📖-zoom-wrap{transform-origin:top left;transform-style:preserve-3d;height:calc(100vh - 120px)}[bindery-view-mode=interactive] .📖-zoom-wrap{transform-origin:center left}.📖-viewing>:not(.📖-root):not(.📖-measure-area){display:none!important}.📖-print-page{page-break-after:always;overflow:hidden;align-items:center;transition:all .2s}.📖-print-page,.📖-spread-wrapper{position:relative;display:flex;justify-content:center}.📖-spread-wrapper{margin:0 auto 32px}.📖-print-page .📖-spread-wrapper{margin:0 auto}.📖-flap-holder{perspective:5000px;position:absolute;top:0;right:0;left:0;bottom:0;margin:auto;transform-style:preserve-3d}.📖-flip-sizer{position:relative;margin:auto;padding:0 20px;box-sizing:content-box;height:100%!important}.📖-page3d{margin:auto;width:var(--bindery-page-width);height:var(--bindery-page-height);transform:rotateY(0);transform-style:preserve-3d;transform-origin:left;transition:transform .5s,box-shadow .1s;position:absolute;left:0;right:0;top:0;bottom:0}.📖-page3d:hover{box-shadow:2px 0 4px rgba(0,0,0,.2)}.📖-page3d.flipped{transform:rotateY(-180deg)}.📖-page3d .📖-page{position:absolute;backface-visibility:hidden;-webkit-backface-visibility:hidden;box-shadow:none}.📖-page3d .📖-page3d-front{transform:rotateY(0)}.📖-page3d .📖-page3d-back{transform:rotateY(-180deg)}.📖-print-mark-wrap{display:none;position:absolute;pointer-events:none;top:0;bottom:0;left:0;right:0;z-index:3}.📖-show-bleed-marks .📖-print-mark-wrap,.📖-show-bleed-marks .📖-print-mark-wrap>[class*=bleed],.📖-show-crop .📖-print-mark-wrap,.📖-show-crop .📖-print-mark-wrap>[class*=crop]{display:block}.📖-print-mark-wrap>div{display:none;position:absolute;overflow:hidden}.📖-print-mark-wrap>div:after,.📖-print-mark-wrap>div:before{content:\"\";display:block;position:absolute}.📖-print-mark-wrap>div:before{top:0;left:0}.📖-print-mark-wrap>div:after{bottom:0;right:0}.📖-bleed-left,.📖-bleed-right,.📖-crop-fold,.📖-crop-left,.📖-crop-right{width:1px;margin:auto}.📖-bleed-left:after,.📖-bleed-left:before,.📖-bleed-right:after,.📖-bleed-right:before,.📖-crop-fold:after,.📖-crop-fold:before,.📖-crop-left:after,.📖-crop-left:before,.📖-crop-right:after,.📖-crop-right:before{width:1px;height:12pt;background-image:linear-gradient(90deg,#000 0,#000 51%,transparent 0);background-size:1px 100%}.📖-bleed-bottom,.📖-bleed-top,.📖-crop-bottom,.📖-crop-top{height:1px}.📖-bleed-bottom:after,.📖-bleed-bottom:before,.📖-bleed-top:after,.📖-bleed-top:before,.📖-crop-bottom:after,.📖-crop-bottom:before,.📖-crop-top:after,.📖-crop-top:before{width:12pt;height:1px;background-image:linear-gradient(180deg,#000 0,#000 51%,transparent 0);background-size:100% 1px}.📖-crop-fold{right:0;left:0}.📖-crop-left{left:0}.📖-crop-right{right:0}.📖-crop-top{top:0}.📖-crop-bottom{bottom:0}.📖-print-meta{padding:12pt;text-align:center;font-family:-apple-system,BlinkMacSystemFont,Roboto,sans-serif;font-size:8pt;display:block!important;position:absolute;bottom:-60pt;left:0;right:0}");
+
+/* global BINDERY_VERSION */
+
+var Bindery = function () {
+ function Bindery() {
+ var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ classCallCheck(this, Bindery);
+
+ console.log('\uD83D\uDCD6 Bindery ' + BINDERY_VERSION);
+
+ this.autorun = opts.autorun || true;
+ this.autoupdate = opts.autoupdate || false;
+ scheduler.isDebugging = opts.debug || urlQuery('debug') || false;
+
+ OptionType.validate(opts, {
+ name: 'makeBook',
+ autorun: OptionType.bool,
+ content: OptionType.any,
+ ControlsComponent: OptionType.any,
+ pageSetup: OptionType.shape({
+ name: 'pageSetup',
+ bleed: OptionType.length,
+ margin: OptionType.shape({
+ name: 'margin',
+ top: OptionType.length,
+ inner: OptionType.length,
+ outer: OptionType.length,
+ bottom: OptionType.length
+ }),
+ size: OptionType.shape({
+ name: 'size',
+ width: OptionType.length,
+ height: OptionType.length
+ })
+ }),
+ view: OptionType.enum.apply(OptionType, toConsumableArray(Object.values(Mode))),
+ printSetup: OptionType.shape({
+ name: 'printSetup',
+ layout: OptionType.enum.apply(OptionType, toConsumableArray(Object.values(Layout))),
+ marks: OptionType.enum.apply(OptionType, toConsumableArray(Object.values(Marks))),
+ paper: OptionType.enum.apply(OptionType, toConsumableArray(Object.values(Paper)))
+ }),
+ rules: OptionType.array
+ });
+
+ this.pageSetup = new PageSetup(opts.pageSetup);
+ this.pageSetup.setupPaper(opts.printSetup);
+
+ var startLayout = opts.printSetup ? opts.printSetup.layout || Layout.PAGES : Layout.PAGES;
+ var startMarks = opts.printSetup ? opts.printSetup.marks || Marks.CROP : Marks.CROP;
+ this.viewer = new Viewer({
+ bindery: this,
+ mode: opts.view || Mode.PREVIEW,
+ marks: startMarks,
+ layout: startLayout,
+ ControlsComponent: opts.ControlsComponent
+ });
+
+ this.rules = defaultRules;
+ if (opts.rules) this.addRules(opts.rules);
+
+ if (!opts.content) {
+ this.viewer.displayError('Content not specified', 'You must include a source element, selector, or url');
+ console.error('Bindery: You must include a source element or selector');
+ } else if (typeof opts.content === 'string') {
+ this.source = document.querySelector(opts.content);
+ if (!(this.source instanceof HTMLElement)) {
+ this.viewer.displayError('Content not specified', 'Could not find element that matches selector "' + opts.content + '"');
+ console.error('Bindery: Could not find element that matches selector "' + opts.content + '"');
+ return;
+ }
+ if (this.autorun) {
+ this.makeBook();
+ }
+ } else if (_typeof(opts.content) === 'object' && opts.content.url) {
+ var url = opts.content.url;
+ var selector = opts.content.selector;
+ this.fetchSource(url, selector);
+ } else if (opts.content instanceof HTMLElement) {
+ this.source = opts.content;
+ if (this.autorun) {
+ this.makeBook();
+ }
+ } else {
+ console.error('Bindery: Source must be an element or selector');
+ }
+ }
+
+ // Convenience constructor
+
+
+ createClass(Bindery, [{
+ key: 'fetchSource',
+ value: function fetchSource(url, selector) {
+ var _this = this;
+
+ fetch(url).then(function (response) {
+ if (response.status === 404) {
+ _this.viewer.displayError('404', 'Could not find file at "' + url + '"');
+ } else if (response.status === 200) {
+ return response.text();
+ }
+ return '';
+ }).then(function (fetchedContent) {
+ var wrapper = document.createElement('div');
+ wrapper.innerHTML = fetchedContent;
+ _this.source = wrapper.querySelector(selector);
+ if (!(_this.source instanceof HTMLElement)) {
+ _this.viewer.displayError('Source not specified', 'Could not find element that matches selector "' + selector + '"');
+ console.error('Bindery: Could not find element that matches selector "' + selector + '"');
+ return;
+ }
+ if (_this.autorun) {
+ _this.makeBook();
+ }
+ }).catch(function (error) {
+ console.error(error);
+ var scheme = window.location.href.split('://')[0];
+ if (scheme === 'file') {
+ _this.viewer.displayError('Can\'t fetch content from "' + url + '"', 'Web pages can\'t fetch content unless they are on a server.');
+ }
+ });
+ }
+ }, {
+ key: 'cancel',
+ value: function cancel() {
+ this.viewer.cancel();
+ document.body.classList.remove(c('viewing'));
+ this.source.style.display = '';
+ }
+ }, {
+ key: 'addRules',
+ value: function addRules(newRules) {
+ var _this2 = this;
+
+ newRules.forEach(function (rule) {
+ if (rule instanceof Rules.Rule) {
+ _this2.rules.push(rule);
+ } else {
+ throw Error('Bindery: The following is not an instance of Bindery.Rule and will be ignored: ' + rule);
+ }
+ });
+ }
+ }, {
+ key: 'updateBookSilent',
+ value: function updateBookSilent() {
+ var _this3 = this;
+
+ this.layoutComplete = false;
+
+ this.source.style.display = '';
+ var content = this.source.cloneNode(true);
+ this.source.style.display = 'none';
+
+ document.body.classList.add(c('viewing'));
+
+ this.pageSetup.updateStylesheet();
+
+ paginate$1({
+ content: content,
+ rules: this.rules,
+ success: function success(book) {
+ _this3.viewer.book = book;
+ _this3.viewer.render();
+ _this3.layoutComplete = true;
+ },
+ progress: function progress() {},
+ error: function error(_error) {
+ _this3.layoutComplete = true;
+ _this3.viewer.displayError('Layout failed', _error);
+ }
+ });
+ }
+ }, {
+ key: 'makeBook',
+ value: function makeBook(doneBinding) {
+ var _this4 = this;
+
+ if (!this.source) {
+ document.body.classList.add(c('viewing'));
+ return;
+ }
+
+ this.layoutComplete = false;
+
+ if (!this.pageSetup.isSizeValid()) {
+ this.viewer.displayError('Page is too small', 'Size: ' + JSON.stringify(this.pageSize) + ' \n Margin: ' + JSON.stringify(this.pageMargin) + ' \n Try adjusting the sizes or units.');
+ console.error('Bindery: Cancelled pagination. Page is too small.');
+ return;
+ }
+
+ this.source.style.display = '';
+ var content = this.source.cloneNode(true);
+ this.source.style.display = 'none';
+
+ // In case we're updating an existing layout
+ this.viewer.clear();
+
+ document.body.classList.add(c('viewing'));
+ if (scheduler.isDebugging) document.body.classList.add(c('debug'));
+
+ this.pageSetup.updateStylesheet();
+
+ this.viewer.setInProgress();
+
+ paginate$1(content, this.rules).progress(function (book) {
+ _this4.viewer.renderProgress(book);
+ }).then(function (book) {
+ _this4.viewer.book = book;
+ _this4.viewer.render();
+
+ _this4.layoutComplete = true;
+ if (doneBinding) doneBinding();
+ _this4.viewer.element.classList.remove(c('in-progress'));
+ document.body.classList.remove(c('debug'));
+ }).catch(function (error) {
+ _this4.layoutComplete = true;
+ _this4.viewer.element.classList.remove(c('in-progress'));
+ _this4.viewer.displayError('Layout couldn\'t complete', error);
+ });
+ }
+ }], [{
+ key: 'makeBook',
+ value: function makeBook() {
+ var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+
+ opts.autorun = opts.autorun ? opts.autorun : true;
+ return new Bindery(opts);
+ }
+ }]);
+ return Bindery;
+}();
+
+Bindery.version = BINDERY_VERSION;
+
+var BinderyWithRules = Object.assign(Bindery, Rules);
+BinderyWithRules.View = Mode;
+BinderyWithRules.Paper = Paper;
+BinderyWithRules.Layout = Layout;
+BinderyWithRules.Marks = Marks;
+
+return BinderyWithRules;
+
+})));
+//# sourceMappingURL=bindery.umd.js.map
diff --git a/assets/script.js b/libreto/assets/script.js
similarity index 87%
rename from assets/script.js
rename to libreto/assets/script.js
index ba9223a..a717cd6 100644
--- a/assets/script.js
+++ b/libreto/assets/script.js
@@ -4,11 +4,7 @@ $(document ).ready( function() {
$(this).parent().addClass('active').siblings().removeClass('active');
var name = $(this).attr('data-name-encoded');
var currentUrl = window.location.href.split('://')[1].split('/');
- if (use_subdomain) {
- var newUrl = '/' + name;
- } else {
- var newUrl = '/' + currentUrl[1] + '/' + name;;
- }
+ var newUrl = '/' + currentUrl[1] + '/' + name;
window.history.pushState("", "", newUrl);
$('article').addClass('loading');
});
diff --git a/assets/style-index.css b/libreto/assets/style-index.css
similarity index 92%
rename from assets/style-index.css
rename to libreto/assets/style-index.css
index 11be227..72d132d 100644
--- a/assets/style-index.css
+++ b/libreto/assets/style-index.css
@@ -63,7 +63,7 @@ Jean-Luc typeface Copyright (c) 2010 Atelier Carvalho Bernau
body {
font-family: "HKGrotesk";
- font-size: 18pt;
+ font-size: 16pt;
padding: 0;
margin: 0;
}
@@ -132,34 +132,23 @@ form {
input[type=input] {
font-family: "HKGrotesk";
- font-size: 18pt;
+ font-size: 16pt;
background: none;
- border: 1px solid #00da53;
- padding: 1rem 2rem;
+ border: 1px solid #000;
+ padding: 1rem;
width: 100%;
margin-bottom: 1rem;
}
button[type=submit] {
font-family: "HKGrotesk";
- font-size: 18pt;
- background: #00da53;
+ font-size: 16pt;
+ background: #000;
color: #fff;
border: 0px solid #ccc;
- padding: 1rem 2rem;
+ padding: 1rem;
width: 100%;
margin-bottom: 1rem;
cursor: pointer;
border-radius: 2rem;
}
-
-div[class^="lang-"] {
- display: none;
-}
-.current-lang-fr .lang-fr {
- display: block;
-}
-
-.current-lang-en .lang-en {
- display: block;
-}
diff --git a/assets/style-reader.css b/libreto/assets/style-reader.css
similarity index 93%
rename from assets/style-reader.css
rename to libreto/assets/style-reader.css
index 3491201..faccd2d 100644
--- a/assets/style-reader.css
+++ b/libreto/assets/style-reader.css
@@ -30,7 +30,10 @@
body {
font-family: 'HKGrotesk';
font-size: 16px;
- padding: 1rem;
+}
+
+main {
+ padding: 1rem;
max-width: 40rem;
}
@@ -52,3 +55,10 @@ hr {
border-bottom: 1px solid #ccc;
margin-bottom: 1rem;
}
+
+img {
+ max-width: 100%;
+ height: auto;
+ margin: auto;
+ display: block;
+}
diff --git a/assets/style.css b/libreto/assets/style.css
similarity index 86%
rename from assets/style.css
rename to libreto/assets/style.css
index bd05d2e..7361ec7 100644
--- a/assets/style.css
+++ b/libreto/assets/style.css
@@ -83,16 +83,31 @@ a {
hr {
border: none;
- border-bottom: 1px solid #ccc;
- margin-bottom: 1rem;
+ border-bottom: 1px solid #ddd;
+ margin: 1rem 0 ;
+}
+
+code {
+ font-size: .8rem;
+ padding: .1rem .3rem;
+ background: #eee;
+ font-family: 'whoismono', monospace;
+ border: .5px solid #ddd;
+ margin: .1rem 0;
+ display: inline-block;
+}
+
+header p {
+ margin: 0;
}
header ul {
- margin: 1rem;
+ margin: 0;
+ margin-bottom: 1rem;
}
header li {
list-style-type: "– ";
- margin-left: 1.1rem;
+ margin-left: .8rem;
list-style-position: outside;
}
@@ -102,7 +117,8 @@ header u {
}
header .full a {
- border-bottom: 1px solid #ccc;
+ border: 0;
+ text-decoration: underline;
}
#container {
@@ -116,18 +132,18 @@ header .full a {
header {
flex: 2;
- padding: 1rem;
- padding-top: 3rem;
+ padding: 3rem 1.5rem 1.5rem 1.5rem;
overflow-y: auto;
overflow-x: hidden;
z-index: 2;
- border-right: 1px solid #ccc;
+ border-right: 1px solid #ddd;
position: relative;
+ font-size: 1rem;
}
nav {
flex: 1;
- border-right: 1px solid #ccc;
+ border-right: 1px solid #ddd;
flex-direction: column;
display: flex;
}
@@ -156,7 +172,7 @@ nav li {
font-family: 'spungold';
font-size: 1.2rem;
padding: 1rem;
- border-bottom: 1px solid #ccc;
+ border-bottom: 1px solid #ddd;
}
nav li.active a {
@@ -164,12 +180,12 @@ nav li.active a {
}
nav li.settings {
- background-color: #ccc;
- border-bottom: 1px solid #B3B3B3;
+ background-color: #eee;
+ border-bottom: 1px solid #ddd;
}
nav li.settings:last-child {
- border-bottom: 1px solid #ccc;
+ border-bottom: 1px solid #ddd;
}
nav ul.menu {
@@ -177,8 +193,8 @@ nav ul.menu {
}
nav ul.helpers {
- border-top: 1px solid #ccc;
- border-bottom: 1px solid #ccc;
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
margin-top: -1px;
}
@@ -192,7 +208,7 @@ nav ul.helpers li {
nav ul.helpers li {
border: 0;
- border-right: 1px solid #ccc;
+ border-right: 1px solid #ddd;
}
nav ul.helpers li:last-child {
@@ -205,6 +221,11 @@ iframe {
border: none;
}
+header .full {
+ margin-bottom: 3rem;
+ font-size: .9rem;
+}
+
header .colophon {
font-size: .8rem;
display: flex;
@@ -229,6 +250,7 @@ header.close div.full {
header.close {
flex: none;
+ padding: 1rem;
}
header.close div {
diff --git a/assets/texts/colophon.md b/libreto/assets/texts/colophon.md
similarity index 100%
rename from assets/texts/colophon.md
rename to libreto/assets/texts/colophon.md
diff --git a/assets/texts/homepage.md b/libreto/assets/texts/homepage.md
similarity index 100%
rename from assets/texts/homepage.md
rename to libreto/assets/texts/homepage.md
diff --git a/assets/texts/translation.yml b/libreto/assets/texts/translation.yml
similarity index 100%
rename from assets/texts/translation.yml
rename to libreto/assets/texts/translation.yml
diff --git a/libreto/bootstrap.php b/libreto/bootstrap.php
new file mode 100644
index 0000000..51d701f
--- /dev/null
+++ b/libreto/bootstrap.php
@@ -0,0 +1,13 @@
+router()->template();
+ $language = $libreto->language();
+ $filename = $template . "." . $language . ".yml";
+ $dictionnary = Spyc::YAMLLoad(ROOT . DS . 'views' . DS . 'languages' . DS . $filename);
+ if(array_key_exists($keyword, $dictionnary)):
+ $text = $dictionnary[$keyword];
+ return ($markdown ? parsedown($text) : $text);
+ else:
+ return $keyword;
+ endif;
+}
+
+function parsedown($markdown){
+ global $Parsedown;
+ $html = $Parsedown
+ ->setBreaksEnabled(true) # enables automatic line breaks
+ ->text($markdown);
+ return $html;
+}
+
+function startsWith($haystack, $needle)
+{
+ $length = strlen($needle);
+ return (substr($haystack, 0, $length) === $needle);
+}
+
+function endsWith($haystack, $needle)
+{
+ $length = strlen($needle);
+
+ return $length === 0 ||
+ (substr($haystack, -$length) === $needle);
+}
diff --git a/libreto/libreto.php b/libreto/libreto.php
new file mode 100644
index 0000000..3961bda
--- /dev/null
+++ b/libreto/libreto.php
@@ -0,0 +1,179 @@
+ "Libreto",
+ 'url' => $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER["SERVER_NAME"],
+ 'default_provider' => 'framapad',
+ );
+
+ return $defaults;
+ }
+
+ public function __construct($options) {
+
+ $this->pads = new Pads();
+ $this->router = new Router();
+ $this->options = array_merge($this->defaults(), $options);
+
+ $this->set_provider();
+ $this->set_name();
+ $this->set_language();
+ $this->set_mode();
+
+ }
+
+ public function launch(){
+
+ $this->set_menu();
+ $this->set_pads();
+
+ // Load template
+ include ROOT . DS . "views" . DS . "templates" . DS . $this->router()->template() . ".php";
+
+ }
+
+ public function set_provider(){
+ $name = $this->router()->provider() ?: $this->options('default_provider');
+ $providers = $this->options('providers');
+ if ($name && array_key_exists($name, $providers)) :
+ $this->provider = $providers[$name];
+ endif;
+ return;
+ }
+
+ public function provider($key = 'url'){
+ return $this->provider[$key];
+ }
+
+ public function set_name(){
+ $this->name = $this->router()->name();
+ }
+
+ public function set_language() {
+ $language = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
+ switch ($language){
+ case "fr":
+ $this->language = "fr";
+ return "fr";
+ break;
+
+ default:
+ $this->language = "en";
+ return "en";
+ break;
+ }
+ }
+
+ public function language()
+ {
+ return $this->language;
+ }
+
+ public function set_mode() {
+ if (!isset($_SESSION['mode'])) {
+ $_SESSION['mode'] = 'read';
+ }
+ if(isset($_GET['mode'])):
+ if($_GET['mode'] == "write") :
+ $_SESSION['mode'] = 'write';
+ else :
+ $_SESSION['mode'] = 'read';
+ endif;
+ // redirect to same page without url parameters
+ $url = strtok($_SERVER["REQUEST_URI"],'?');
+ header('Location: ' . $url );
+ endif;
+ $this->mode = $_SESSION['mode'];
+ }
+
+ public function set_menu(){
+ $this->pads()->push('Menu (type: settings visibility: private)');
+ $pad = $this->pads()->find('Menu');
+ $menu = array_values(array_filter(preg_split('/\r\n|\r|\n/', $pad->txt())));
+ $menu = array_map('trim', $menu);
+ // if menu is filled with default text, make it empty
+ if(count($menu)):
+ if( strpos($menu[0], $this->provider('default_text')) === 0 ) :
+ $menu = array();
+ endif;
+ endif;
+ $this->menu = $menu;
+ }
+
+ public function set_pads(){
+ $this->pads->push($this->menu);
+ // find the requested pad or else the first of the menu list
+ $pad = $this->router()->pad() ?: $this->pads->first();
+ // select requested pad
+ if ($pad):
+ $pad->select();
+ endif;
+ }
+
+ public function router(){
+ // Router
+ return $this->router;
+ }
+
+ public function name() {
+ return $this->name;
+ }
+
+ public function pads() {
+ return $this->pads;
+ }
+
+ public function credits() {
+ $Parsedown->text(file_get_contents("/libreto/assets/texts/colophon.md"));
+ }
+
+ public function mode(){
+ return $this->mode;
+ }
+
+ public function options($key){
+ if(array_key_exists($key, $this->options)):
+ return $this->options[$key];
+ endif;
+ return;
+ }
+
+ public function export(){
+
+ $title = $this->name();
+ $introduction = $this->pads()->find('about')->html();
+
+ $pads = $this->pads()->children();
+ $chapters = array();
+ foreach($pads as $pad) :
+ $chapter = $pad->name();
+ $html = $pad->html();
+ $chapters[] = array("title" => $chapter, "html" => $html);
+ endforeach;
+
+ $odt = new \CatoTH\HTML2OpenDocument\Text();
+ $odt->addHtmlTextBlock('' . $title . ' ');
+ $odt->addHtmlTextBlock($introduction);
+ foreach ($chapters as $chapter) {
+ $odt->addHtmlTextBlock('' . $chapter['title'] . ' ');
+ $odt->addHtmlTextBlock($chapter['html']);
+ }
+ $odt->finishAndOutputOdt('libreto-' . $title . '.odt');
+
+ }
+
+}
diff --git a/libreto/pad.php b/libreto/pad.php
new file mode 100644
index 0000000..989abb5
--- /dev/null
+++ b/libreto/pad.php
@@ -0,0 +1,166 @@
+color = "#000";
+ $this->options = $this->defaults();
+ $this->name = trim(preg_replace('/(?=[^\]])\([a-z0-9_-]+:.*?\)/', '', $input));
+
+ $search = preg_match('/(?=[^\]])\([a-z0-9_-]+:.*?\)/', $input, $options);
+ if($search):
+ $this->set_options($options[0]);
+ endif;
+ $this->set_urls();
+
+ }
+
+ public function defaults(){
+ $defaults = array(
+ 'color' => "#000000",
+ 'type' => "default",
+ 'url' => false,
+ 'visibility' => 'visible'
+ );
+
+ return $defaults;
+ }
+
+ public function set_options($options) {
+ $tag = trim( rtrim( ltrim( $options, '(' ), ')' ) );
+ $attributes = "color|url|type|visibility";
+ $search = preg_split('!(' . $attributes . '):!i', $tag, false, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
+
+ $num = 0;
+ $attr = array();
+ // get an associative array attribute=>value
+ foreach($search as $key) :
+ if(!isset($search[$num+1])) break;
+ $key = trim($search[$num]);
+ $value = trim($search[$num+1]);
+ $this->options[$key] = $value;
+ $num = $num+2;
+ endforeach;
+ }
+
+ public function options($key){
+ if(array_key_exists($key, $this->options)):
+ return $this->options[$key];
+ endif;
+ return;
+ }
+
+ public function set_urls() {
+ global $libreto;
+
+ if($this->options['url']):
+ $title = array_slice(explode('/', trim('/', $this->options['url'])), -1)[0];
+ $url = trim($this->options['url'], '/ ');
+ else:
+ $title = urlencode(strtolower($libreto->options('name'))) . "+" . urlencode(strtolower($libreto->name())) . "+" . urlencode(strtolower($this->name));
+ $url = $libreto->provider('url') . "/p/" . $title;
+ endif;
+
+ $this->url = array(
+ "pad" => $url . $libreto->options('pads_params'),
+ "reader" => "/reader/" . urlencode($libreto->name()) . '/' . urlencode($this->name),
+ "txt" => $url . "/export/txt",
+ "markdown" => $url . "/export/markdown"
+ );
+
+ }
+
+ public function type(){
+ if(array_key_exists('type', $this->options)):
+ return $this->options['type'];
+ endif;
+ return false;
+ }
+
+ public function color(){
+ if(array_key_exists('color', $this->options)):
+ return $this->options['color'];
+ endif;
+ return false;
+ }
+
+ public function select() {
+ $this->selected = true;
+ return $this;
+ }
+
+ public function selected(){
+ return $this->selected;
+ }
+
+ public function url($format = 'pad') {
+ global $libreto;
+ if($format == "pad"):
+ if($libreto->mode() == "read"):
+ return $this->url['reader'];
+ else:
+ return $this->url['pad'];
+ endif;
+ endif;
+ return $this->url[$format];
+ }
+
+ public function name() {
+ return $this->name;
+ }
+
+ public function txt() {
+ $txt = file_get_contents($this->url('txt'));
+ return $txt;
+ }
+
+ public function html() {
+
+ global $Parsedown, $Purifier;
+
+ $markdown = file_get_contents($this->url('markdown'));
+ // remove \url{} tags
+ $markdown = preg_replace('#\\\url\{(.+)\}#i', '$1', $markdown);
+ // replace underline tags
+ $markdown = preg_replace('#underline(.+)underline#', '$1 ', $markdown);
+ // strip slashes
+ $markdown = stripslashes($markdown);
+ // parse
+ $html = $Parsedown->text($markdown);
+ // sanitize
+ $html = $Purifier->purify($html);
+ // return
+ return $html;
+
+ }
+
+ public function css(){
+ $css = strip_tags(file_get_contents($this->url('txt')));
+ return $css;
+ }
+
+ public function visible($filter = false){
+ global $libreto;
+ if($filter) :
+ $filter = (array)$filter;
+ else:
+ $filter = $libreto->mode() == 'read' ? array('visible') : array('visible', 'private');
+ endif;
+
+ if(in_array($this->options('visibility'), $filter ))
+ {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/libreto/pads.php b/libreto/pads.php
new file mode 100644
index 0000000..616a9d0
--- /dev/null
+++ b/libreto/pads.php
@@ -0,0 +1,83 @@
+push($pads);
+ }
+
+ public function push($pads){
+ $pads = (array)$pads;
+ foreach ($pads as $pad) :
+ if(endsWith($pad, '.css')):
+ $this->pads[] = new Pad($pad . ' (type: settings visibility: private)');
+ else:
+ $this->pads[] = new Pad($pad);
+ endif;
+ endforeach;
+ return $this;
+ }
+
+ public function css() {
+ if($css = $this->find('style.css')):
+ return $css->css();
+ endif;
+ return false;
+ }
+
+ public function find($p){
+ foreach($this->pads as $pad) :
+ if (strtolower($pad->name()) == strtolower($p)):
+ return $pad;
+ endif;
+ endforeach;
+ return false;
+ }
+
+ public function children($filter = false) {
+ global $libreto;
+ $pads = array_filter($this->pads, function($pad) use ($filter) {
+ if($pad->visible($filter)) { return true; }
+ return false;
+ });
+ $pads = array_values($pads);
+ return $pads;
+ }
+
+ public function first() {
+ $pads = $this->children();
+ if(array_key_exists(0, $pads)):
+ return $pads[0];
+ endif;
+ return;
+ }
+
+ public function selected(){
+ foreach ($this->pads as $pad) {
+ if($pad->selected()) { return $pad; }
+ }
+ return false;
+ }
+
+ public function selectActivePad() {
+
+ global $libreto;
+
+ // set default pad
+ $default = $this->first() ?: $this->find("help");
+
+ // set requested pad
+ $pad = $libreto->router()->pad() ?: $default;
+
+ $pad->select();
+
+ return $pad;
+
+ }
+
+}
diff --git a/libreto/router.php b/libreto/router.php
new file mode 100644
index 0000000..a7ff709
--- /dev/null
+++ b/libreto/router.php
@@ -0,0 +1,96 @@
+split();
+ }
+
+ public function split() {
+
+ // directories
+ $directories = explode( '/', trim($_SERVER['REQUEST_URI'], '/') );
+ $this->directories = array_filter(array_map("urldecode", $directories));
+
+ // subdomain
+ $server_name = explode(".",$_SERVER["SERVER_NAME"]);
+ if (array_key_exists(0, $server_name) && array_key_exists(2, $server_name)):
+ $name = urldecode($server_name[0]);
+ $this->subdomain = strtok( $name ,'?');
+ endif;
+
+ }
+
+ public function provider(){
+ return $this->subdomain;
+ }
+
+ public function template(){
+ if( array_key_exists(0, $this->directories) ):
+ $templates = glob(ROOT . DS . "views" . DS . "templates" . DS . "*.php");
+ $templates = array_map(function($file) {
+ return basename($file, ".php");
+ }, $templates);
+ $directory = strtolower($this->directories[0]);
+ if( in_array($directory, $templates) ):
+ return $directory;
+ else:
+ return "libreto";
+ endif;
+ else:
+ return "home";
+ endif;
+ }
+
+ public function name(){
+
+ global $libreto;
+
+ if($this->template() != "libreto"):
+ $n = 1;
+ else:
+ $n = 0;
+ endif;
+
+ if (array_key_exists($n + 0, $this->directories)):
+ $name = urldecode($this->directories[$n + 0]);
+ $name = strtok( $name ,'?');
+ return $name;
+ else:
+ return false;
+ endif;
+
+ }
+
+ public function pad(){
+
+ global $libreto;
+
+ if($this->template() != "libreto"):
+ $n = 1;
+ else:
+ $n = 0;
+ endif;
+
+ if (array_key_exists($n + 1, $this->directories)):
+ $name = $this->directories[$n + 1];
+ else:
+ return false;
+ endif;
+
+ if ($pad = $libreto->pads()->find($name)):
+ if ($pad->visible()) :
+ return $pad;
+ endif;
+ else:
+ return false;
+ endif;
+
+ }
+
+}
diff --git a/libreto/vendor/load.php b/libreto/vendor/load.php
new file mode 100644
index 0000000..79b4fb3
--- /dev/null
+++ b/libreto/vendor/load.php
@@ -0,0 +1,12 @@
+setBreaksEnabled(true);
diff --git a/reader.php b/reader.php
deleted file mode 100644
index d70983d..0000000
--- a/reader.php
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
- Pad reader
-
- = $css_name ? "" : "" ?>
-
-
-
- = $html ?>
-
-
-
diff --git a/snippets/controller.php b/snippets/controller.php
deleted file mode 100644
index 8c9d831..0000000
--- a/snippets/controller.php
+++ /dev/null
@@ -1,8 +0,0 @@
-setBreaksEnabled(true);
-
-function get_homepage() {
- $homepage = get_html(get_url("introduction", "markdown"));
- $exerpt = trim(strip_tags($homepage));
- if(strpos($exerpt, "–––––") === 0):
- $homepage = get_html(get_url("help", "markdown"));
- endif;
- return $homepage;
-}
-
-function get_pads_list() {
- global $libreto_name, $custom_css;
- $pads_list = file(get_url("menu", "txt"));
- $pads_list = array_map('trim', $pads_list);
- // if pad is filled with default text, make pads_list empty
- $firstline = $pads_list[0];
- if( strpos($firstline, "–––––") === 0 ) :
- $pads_list = false;
- endif;
- // if a pad is named style.css, use it as css file
- if($pads_list):
- foreach ($pads_list as $key=>$pad_name) :
- if($pad_name == "style.css"):
- array_splice($pads_list, $key, 1);
- $custom_css = get_url($pad_name, "txt");
- endif;
- endforeach;
- endif;
- return $pads_list;
-}
-
-function get_url($name, $format = "pad") {
- global $pads_host, $instance_name, $pads_params, $libreto_name;
-
- $server = "http://" . $_SERVER["SERVER_NAME"];
- $pads_prefix = urlencode(strtolower($instance_name)) . "+" . urlencode($libreto_name) . "+";
- $pad_name = $pads_prefix . urlencode($name);
- $css_name = $pads_prefix . urlencode("style.css");
- $pad_url = $pads_host . "/p/" . $pad_name;
-
- if($name == 'help') :
- $pad_url = "https://annuel2.framapad.org/p/libreto-help";
- if ($format == "markdown") {
- return $pad_url . "/export/markdown";
- } else {
- return $server . "/reader.php?host=$pads_host&pad=libreto-help&css=$css_name";
- }
- endif;
-
- switch ($format) :
- case 'pad':
- if($_SESSION['mode'] == "write") :
- return $pad_url . $pads_params ;
- else:
- //var_dump($pad_url);
- return $server . "/reader.php?host=$pads_host&pad=$pad_name&css=$css_name" ;
- endif;
- break;
-
- default:
- return $pad_url . "/export/" . $format ;
- break;
- endswitch;
-}
-
-function get_html($markdown_url) {
- global $Parsedown, $Purifier;
- $markdown = file_get_contents($markdown_url);
- // remove \url{} tags
- $markdown = preg_replace('#\\\url\{(.+)\}#i', '$1', $markdown);
- // replace underline tags
- $markdown = preg_replace('#underline(.+)underline#', '$1 ', $markdown);
- // remove slashes
- $markdown = stripslashes($markdown);
- $html = $Parsedown->text($markdown);
- // sanitize html
- $html = $Purifier->purify($html);
- return $html;
-}
-
-function export() {
- global $libreto_name, $homepage, $pads_list;
- $title = $libreto_name;
- $introduction = $homepage;
-
- $chapters = $pads_list;
- $chapters = array_map(function($chapter){
- $url = get_url($chapter, 'markdown');
- $html = get_html($url);
- return array("title" => $chapter, "content" => $html);
- }, $chapters);
-
- $odt = new \CatoTH\HTML2OpenDocument\Text();
- $odt->addHtmlTextBlock('' . $title . ' ');
- $odt->addHtmlTextBlock($homepage);
- foreach ($chapters as $chapter) {
- $odt->addHtmlTextBlock('' . $chapter['title'] . ' ');
- $odt->addHtmlTextBlock($chapter['content']);
- }
- $odt->finishAndOutputOdt('Libreto-' . $title . '.odt');
-}
-
-function get_current_pad_name() {
- global $use_subdomain, $pads_list;
- if(is_array($pads_list)):
- $default_pad = $pads_list[0];
- else:
- $default_pad = false;
- endif;
- $uri = explode( '/', trim($_SERVER['REQUEST_URI'], '/') );
- if($use_subdomain) :
- if (array_key_exists(0, $uri)):
- $uri = urldecode($uri[0]);
- $pad_name = in_array($uri, $pads_list) ? $uri : false;
- else :
- $pad_name = $default_pad;
- endif;
- else :
- if (array_key_exists(1, $uri)):
- $uri = urldecode($uri[1]);
- if(is_array($pads_list)) :
- $pad_name = in_array($uri, $pads_list) ? $uri : false;
- else :
- $pad_name = false;
- endif;
- else :
- $pad_name = $default_pad;
- endif;
- endif;
- return $pad_name;
-}
-
-function get_libreto_name() {
- global $use_subdomain;
- if($use_subdomain) :
- $server_name = explode(".",$_SERVER["SERVER_NAME"]);
- if (array_key_exists(0, $server_name) && array_key_exists(1, $server_name)):
- $name = urldecode($server_name[0]);
- $name = strtok( $name ,'?');
- endif;
- else:
- $uri = explode( '/', trim($_SERVER['REQUEST_URI'], '/') );
- if (array_key_exists(0, $uri)):
- $name = urldecode($uri[0]);
- $name = strtok( $name ,'?');
- endif;
- endif;
- return $name;
-}
-
-function get_user_language() {
- $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
- switch ($lang){
- case "fr":
- return "fr";
- break;
- default:
- return "en";
- break;
- }
-}
-
-function load_translation_file() {
- return spyc_load_file("assets/texts/translation.yml");
-}
-
-function localize($key) {
- global $translation, $user_language;
- if (array_key_exists($key, $translation)) :
- if (array_key_exists($user_language, $translation[$key])) :
- return $translation[$key][$user_language];
- elseif (array_key_exists($fallback_language, $translation[$key])) :
- return $translation[$key][$fallback_language];
- endif;
- else:
- return "$key ($user_language)";
- endif;
-}
diff --git a/snippets/header.php b/snippets/header.php
deleted file mode 100644
index 0b99cf9..0000000
--- a/snippets/header.php
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
- = $libreto_name ?> - Libreto
-
-
-
-
-
- = $custom_css ? '' : '' ?>
-
-
diff --git a/snippets/init.php b/snippets/init.php
deleted file mode 100644
index 753d2a1..0000000
--- a/snippets/init.php
+++ /dev/null
@@ -1,30 +0,0 @@
-">
-
- = $homepage ?>
-
- = $Parsedown->text(file_get_contents("./assets/texts/colophon.md")); ?>
-
-
×
-
-
-
diff --git a/snippets/nav.php b/snippets/nav.php
deleted file mode 100644
index c78521b..0000000
--- a/snippets/nav.php
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/views/languages/home.en.yml b/views/languages/home.en.yml
new file mode 100644
index 0000000..0853d14
--- /dev/null
+++ b/views/languages/home.en.yml
@@ -0,0 +1,22 @@
+title: Libreto
+logo : http://libreto.net/assets/images/libretonet.png
+introduction: >
+ Libreto is a collaborative notebook based on [framapad](https://framapad.org).
+
+
+ It can become a mini-website, a workshop logbook or the tool to write a collective book.
+
+
+ Libreto is free and minimal.
+
+ No registration, no connection, anyone can write a Libreto.
+
+create: Create libreto
+
+colophon: >
+
+ Libreto is under [GNU/GPLv3 License](https://www.gnu.org/licenses/gpl-3.0.en.html)
+
+ Use and modify on [GitHub](https://github.com/Ventricule/libreto)
+
+ A project initiated by [Pierre Tandille](http://lecollecteur.fr)
diff --git a/views/languages/home.fr.yml b/views/languages/home.fr.yml
new file mode 100644
index 0000000..0be4227
--- /dev/null
+++ b/views/languages/home.fr.yml
@@ -0,0 +1,26 @@
+title: Libreto
+
+logo : http://libreto.net/assets/images/libretonet.png
+
+introduction: >
+ Libreto est un carnet de note collaboratif fondé sur [etherpad](http://etherpad.org/).
+
+
+ Il peut devenir un mini-site, le carnet de bord d'un workshop ou le support de rédaction d'un livre collectif.
+
+
+ Libreto est libre, gratuit, et minimaliste.
+
+
+ Pas d'inscription, pas de connexion, tout le monde peut écrire un Libreto.
+
+
+create: Créer un Libreto
+
+colophon: >
+
+ Libreto est sous [licence GNU/GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html).
+
+ N'hésitez pas à participer sur [GitHub](https://github.com/Ventricule/libreto).
+
+ Un projet initié par [Pierre Tandille](http://lecollecteur.fr).
diff --git a/views/languages/libreto.en.yml b/views/languages/libreto.en.yml
new file mode 100644
index 0000000..2c889ee
--- /dev/null
+++ b/views/languages/libreto.en.yml
@@ -0,0 +1,45 @@
+title: Libreto
+read: Read
+write: Write
+export: Export
+download: Download
+update: Update
+credits: |
+ **Crédits**
+ Graphisme & programmation : [Pierre Tandille](http://lecollecteur.fr)
+ Typographies : [HK Grotesk](https://hanken.co), [Spun Gold](http://www.co-bay.com/dasein), [Jean-Luc](http://www.carvalho-bernau.com)
+ Pads : [Etherpad](http://etherpad.org/), [Framapad](http://framapad.org), [Fairkom Board](https://board.net/)
+about: |
+ **About Libreto**
+ Un Libreto est un carnet de note collaboratif fondé sur framapad. Libreto est libre, gratuit, et minimaliste. [Plus d'infos](http://libreto.net)
+help: |
+ ## Bienvenue ! — [Libreto](http://libreto.net) est un outil d'écriture et de publication collaboratif basé sur [etherpad](https://etherpad.org).
+
+ **Pour commencer :**
+ - Rédigez la liste de vos pads
+ - Rechargez la page
+ - Lancez-vous : écrivez sur vos pads !
+
+ **Pour partager / collaborer :**
+ - Sélectionnez et copiez l'URL
+ - Partagez-là !
+
+ **Pour diffuser :**
+ - exportez sous forme de livre, de PDF ou de document ODT
+
+ **Pour personnaliser :**
+ - Créez une feuille de style en ajoutant un pad style.css à votre menu
+
+ ## Plus d'options :
+
+ **Menu**
+
+ *Exemple :*
+ Mon pad (url: http://url.pad)
+ *Options :*
+ **url** : adresse d'un pad existant
+ **color** : [nom d'une couleur](https://htmlcolorcodes.com/color-names) / [code hexadecimal](https://htmlcolorcodes.com/color-chart)
+ **visibility** : visible / private / hidden
+
+ **Exporter un livre**
+ Créez une feuille de style pour le livre en ajoutant un pad book.css à votre menu
diff --git a/views/languages/libreto.fr.yml b/views/languages/libreto.fr.yml
new file mode 100644
index 0000000..9bc0014
--- /dev/null
+++ b/views/languages/libreto.fr.yml
@@ -0,0 +1,46 @@
+title: Libreto
+read: Lire
+write: Écrire
+export: Exporter
+download: Télécharger
+update: Mettre à jour
+credits: |
+ **Crédits**
+ Graphisme & programmation : [Pierre Tandille](http://lecollecteur.fr)
+ Typographies : [HK Grotesk](https://hanken.co), [Spun Gold](http://www.co-bay.com/dasein), [Jean-Luc](http://www.carvalho-bernau.com)
+ Pads : [Etherpad](http://etherpad.org/), [Framapad](http://framapad.org), [Fairkom Board](https://board.net/)
+about: |
+ **À propos**
+ Un Libreto est un carnet de note collaboratif fondé sur framapad. Libreto est libre, gratuit, et minimaliste. [Plus d'infos](http://libreto.net)
+help: |
+ ## Bienvenue ! — [Libreto](http://libreto.net) est un outil d'écriture et de publication collaboratif basé sur [etherpad](https://etherpad.org).
+
+ **Pour commencer :**
+ - Rédigez la liste de vos pads
+ - Rechargez la page
+ - Lancez-vous : écrivez sur vos pads !
+
+ **Pour partager / collaborer :**
+ - Sélectionnez et copiez l'URL
+ - Partagez-là !
+
+ **Pour diffuser :**
+ - exportez sous forme de livre, de PDF ou de document ODT
+
+ **Pour personnaliser :**
+ - Créez une feuille de style en ajoutant un pad style.css à votre menu
+
+ ----
+
+ **Menu**
+ Pour chaque pad, vous pouvez ajoutez des options entre parenthèses. Exemple :
+ `Mon pad (url: http://url.pad color: blue)`
+ *Options disponibles*
+ **url** : adresse d'un pad existant
+ **color** : [nom d'une couleur](https://htmlcolorcodes.com/color-names) / [code hexadecimal](https://htmlcolorcodes.com/color-chart)
+ **visibility** : visible / private / hidden
+
+ ----
+
+ **Exporter un livre**
+ Créez une feuille de style pour le livre en ajoutant un pad book.css à votre menu
diff --git a/snippets/footer.php b/views/snippets/footer.php
similarity index 100%
rename from snippets/footer.php
rename to views/snippets/footer.php
diff --git a/views/snippets/header.php b/views/snippets/header.php
new file mode 100644
index 0000000..e793266
--- /dev/null
+++ b/views/snippets/header.php
@@ -0,0 +1,12 @@
+
+
+
+
+
+ = $libreto->name() ?> - Libreto
+
+
+
+
+
+
diff --git a/views/snippets/introduction.php b/views/snippets/introduction.php
new file mode 100644
index 0000000..91d4d46
--- /dev/null
+++ b/views/snippets/introduction.php
@@ -0,0 +1,17 @@
+
+
diff --git a/views/snippets/nav.php b/views/snippets/nav.php
new file mode 100644
index 0000000..d34a485
--- /dev/null
+++ b/views/snippets/nav.php
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
diff --git a/views/templates/bindery.php b/views/templates/bindery.php
new file mode 100644
index 0000000..b61c982
--- /dev/null
+++ b/views/templates/bindery.php
@@ -0,0 +1,73 @@
+
+
+
+
+
+ = $libreto->name() ?> - Libreto
+
+
+
+
+
+
+
+
+
+
diff --git a/views/templates/export.php b/views/templates/export.php
new file mode 100644
index 0000000..403251c
--- /dev/null
+++ b/views/templates/export.php
@@ -0,0 +1,3 @@
+export();
diff --git a/views/templates/home.php b/views/templates/home.php
new file mode 100644
index 0000000..127178b
--- /dev/null
+++ b/views/templates/home.php
@@ -0,0 +1,37 @@
+
+options('url') . '/' . urlencode($new_name) );
+ endif;
+endif;
+?>
+
+
+
+
+
+
+
+
+ Libreto
+
+
+
+
+
+
+ = l('introduction') ?>
+
+
+
+
+
+ = l('colophon') ?>
+
+
+
+
diff --git a/views/templates/libreto.php b/views/templates/libreto.php
new file mode 100644
index 0000000..61ebb23
--- /dev/null
+++ b/views/templates/libreto.php
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ pads()->selected()) :
+ $iframe = $libreto->pads()->selected()->url();
+ else :
+ header('Location: ?mode=write');
+ endif;
+ ?>
+
+
+
+
diff --git a/views/templates/reader.php b/views/templates/reader.php
new file mode 100644
index 0000000..53c76e4
--- /dev/null
+++ b/views/templates/reader.php
@@ -0,0 +1,36 @@
+
+
+
+
+
+ = $libreto->name() ?> - Libreto
+
+
+
+
+
+ router()->pad()) :
+ ?>
+ = $libreto->pads()->selected()->html() ?>
+
+ = $this->name() ?>
+ pads()->children('visible');
+ foreach($pads as $pad) :
+ ?>
+ = $pad->name() ?>
+ = $pad->html() ?>
+
+
+
+