Initial commit

This commit is contained in:
sarahgarcin1 2026-01-05 19:33:15 +01:00
commit 388079e6bb
1108 changed files with 330121 additions and 0 deletions

View file

@ -0,0 +1 @@
.DS_Store

View file

@ -0,0 +1,114 @@
# Plugin for sidenotes
This plugin proposes a script for sidenotes in Paged.js.
The plugin creates sidenotes from `span` elements in the text flow. It can be combined with the [inline_notes plugin](https://gitlab.com/csspageweaver/plugins/inline_notes) to create these span elements from listed notes, which are more common in conversion tools like Pandoc.
Note: The plugin moves notes if they overlap or if the last note overflows at the end of the page.
## How to use the plugin
Add this folder to `csspageweaver/plugins/`.
Call the plugin in `csspageweaver/manifest.json`:
```json
"plugins": [
"sidenotes",
// other plugins ...
],
```
## Configuration
In `manifest.json`, you can modify/add some parameters:
```json
"plugins":{
"sidenotes"
},
"pluginsParameters":{
"sidenotes": {
"selector": ".sidenote",
"position": "outside",
"reset": ".chapter",
"align": ".chapter p:first-of-type"
}
},
```
All the parameters are optional.
- `selector` → CSS selector for the note element (must be inline in the HTML), default is `.sidenote`
- `position` → Specifies the position of sidenotes relative to the main text: options are "outside", "inside", "left", "right".
- 'outside': the left pages notes positonned on the margin left and the right pages notes on the margin right. This is the default value.
- 'inside': the left pages notes positonned on the margin right and the right pages notes on the margin left.
- 'left': the notes of both pages are positinoned on the margin left.
- 'right': the notes of both pages are positinoned on the margin left.
- `reset` → CSS selector where you want reset note counter
- `align` → Element to align the first note of the page to, if present on the page
## Notes in HTML
In your HTML, the note must be a `<span>` inserted in the text, like this:
```HTML
Donec tincidunt, odio vel vestibulum sollicitudin, nibh dolor tempor sapien, ac laoreet
sem felis ut purus.&#8239;<span class="sidenote">Vestibulum neque ex, ullamcorper sit
amet diam sed, pharetra laoreet sem.</span> Morbi cursus bibendum consectetur. Nullam vel
lacus congue nibh pulvinar maximus sit amet eu risus. Curabitur semper odio mauris, nec
imperdiet velit pharetra non. Aenean accumsan nulla ac ex iaculis interdum.
```
You can use the [inline_notes` plugin](https://gitlab.com/csspageweaver/plugins/inline_notes) to create these span elements from listed notes, which are more common in conversion tools like Pandoc.
The inline_notes plugin should be called before the sidenotes plugin in the `manifest.json`:
```json
"plugins": [
"inline_notes",
"sidenotes",
// other plugins ...
],
```
## Styling notes
By default, the width of the notes is set on the corresponding margin where the notes are positioned. You can change this width by adjusting the `padding-left` and `padding-right` of your note elements.
To apply specific style depending on the left or right pages, uses the following CSS (where `sidenote` is the class of your notes):
```CSS
.pagedjs_left_page .sidenote {
padding-left: 40px;
padding-right: 20px;
}
.pagedjs_right_page .sidenote {
padding-left: 20px;
padding-right: 40px;
}
```
It's possible to change the styles of call notes and marker notes directly in your stylesheet like in the following code:
```CSS
.pagedjs_sidenote_call{
color: blue;
}
.pagedjs_sidenote_marker{
color: violet;
}
```
## Possible improvement
Currently, there is no way to break a note element across two pages in Paged.js. If all the sidenotes on a page don't fit, the overflowing sidenotes are pushed to the next page. Implementing a feature to split notes across pages would enhance the layout flexibility.

View file

@ -0,0 +1,8 @@
{
"name": "Margin Notes",
"description": "Create margin notes with call & markers",
"author": ["Julie Blanc et Sarah Garcin"],
"licence": "MIT",
"version": "1.0",
"hook": "marginNotes.js"
}

View file

@ -0,0 +1,260 @@
import { Handler } from '/csspageweaver/lib/paged.esm.js';
export default class marginNotes extends Handler {
constructor(chunker, polisher, caller) {
super(chunker, polisher, caller);
this.parameters = cssPageWeaver.features.marginNotes.parameters;
this.notesClass = this.parameters?.selector || ".sidenote";
this.position = this.parameters?.position || "outside";
this.reset = this.parameters?.reset;
this.marker = this.parameters?.marker || ". ";
}
beforeParsed(content) {
let classNotes = this.notesClass;
let marker = this.marker;
if (typeof this.reset === "string" && this.reset.trim() !== "") {
let sections = content.querySelectorAll(this.reset);
sections.forEach(function (section) {
addCallAndMarker(section, classNotes, marker);
});
} else {
addCallAndMarker(content, classNotes, marker);
}
/* NOTE FLOAT ---------------------------------------------------------------------------------- */
let positionRight = 'left: calc(var(--pagedjs-pagebox-width) - var(--pagedjs-margin-left) - var(--pagedjs-margin-right) - 1px); width: var(--pagedjs-margin-right);';
let positionLeft = 'left: calc(var(--pagedjs-margin-left)*-1 - 1px); width: var(--pagedjs-margin-left);'
let notePosition;
switch (this.position) {
case 'inside':
notePosition = '.pagedjs_left_page ' + classNotes + '{' + positionRight + '} \
.pagedjs_right_page ' + classNotes + '{' + positionLeft + '}';
break;
case 'left':
notePosition = '.pagedjs_left_page ' + classNotes + '{' + positionLeft + '} \
.pagedjs_right_page ' + classNotes + '{' + positionLeft + '}';
break;
case 'right':
notePosition = '.pagedjs_left_page ' + classNotes + '{' + positionRight + '} \
.pagedjs_right_page ' + classNotes + '{' + positionRight + '}';
break;
default:
notePosition = '.pagedjs_left_page ' + classNotes + '{' + positionLeft + '} \
.pagedjs_right_page ' + classNotes + '{' + positionRight + '}';
}
/* SPECIFIC CSS ---------------------------------------------------------------------------------- */
addcss('\
' + classNotes + '{\
position: absolute;\
text-align-last: initial;\
box-sizing: border-box;\
}' + notePosition);
} /* end beforeParsed*/
afterPageLayout(pageElement, page, breakToken) {
let classNotes = this.notesClass;
let marker = this.marker;
let notes = pageElement.querySelectorAll(classNotes);
let noteOverflow = false;
let notesHeightAll = [];
if (typeof (notes) != 'undefined' && notes != null && notes.length != 0) {
for (let n = 0; n < notes.length; ++n) {
// Display notes of the page
notes[n].style.display = "inline-block";
// Add height of the notes to array notesHeightAll
let noteHeight = notes[n].offsetHeight;
notesHeightAll.push(noteHeight);
// Add margins of the notes to array notesHeightAll
if (n >= 1) {
let margins = biggestMargin(notes[n - 1], notes[n]);
notesHeightAll.push(margins);
}
}
/* FIT PAGE ------------------------------------------------------------------------------------- */
// Calculate if all notes fit on the page;
let reducer = (accumulator, currentValue) => accumulator + currentValue;
let allHeight = notesHeightAll.reduce(reducer);
let maxHeight = pageElement.querySelectorAll(".pagedjs_page_content")[0].offsetHeight;
if (allHeight > maxHeight) {
/* IF DOESN'T FIT ----------------------------------------------------------------------------- */
// positions all the notes one after the other starting from the top
notes[0].style.top = parseInt(window.getComputedStyle(notes[0]).marginBottom, 10) * -1 + "px";
for (let a = 1; a < notes.length; ++a) {
let notePrev = notes[a - 1];
let newMargin = biggestMargin(notePrev, notes[a]);
let newTop = notePrev.offsetTop + notePrev.offsetHeight - marginNoteTop(notes[a]) + newMargin;
notes[a].style.top = newTop + "px";
}
// alert
let pageNumber = pageElement.dataset.pageNumber;
alert("Rendering issue \n ☞ A marginal note overflow on page " + pageNumber + " (this is because there is too many on this page and paged.js can't breaks notes between pages for now.)");
noteOverflow = true;
} else {
/* PUSH DOWN ---------------------------------------------------- */
for (let i = 0; i < notes.length; ++i) {
if (i >= 1) {
let noteTop = notes[i].offsetTop;
let notePrev = notes[i - 1];
let newMargin = biggestMargin(notes[i], notePrev);
let notePrevBottom = notePrev.offsetTop - marginNoteTop(notePrev) + notePrev.offsetHeight + newMargin;
// Push down the note to bottom if it's over the previous one
if (notePrevBottom > noteTop) {
notes[i].style.top = notePrevBottom + "px";
}
}
}
/* PUSH UP ---------------------------------------------- */
// Height of the page content
let contentHeight = pageElement.querySelectorAll(".pagedjs_page_content")[0].querySelectorAll("div")[0].offsetHeight;
// Check if last note overflow
let nbrLength = notes.length - 1;
let lastNote = notes[nbrLength];
let lastNoteHeight = lastNote.offsetHeight + marginNoteTop(lastNote);
let noteBottom = lastNote.offsetTop + lastNoteHeight;
if (noteBottom > contentHeight) {
// Push up the last note
lastNote.style.top = contentHeight - lastNoteHeight + "px";
// Push up previous note(s) if if it's over the note
for (let i = nbrLength; i >= 1; --i) {
let noteLastTop = notes[i].offsetTop;
let notePrev = notes[i - 1];
let notePrevHeight = notePrev.offsetHeight;
let newMargin = biggestMargin(notePrev, notes[i]);
let notePrevBottom = notePrev.offsetTop + notePrev.offsetHeight + newMargin;
if (notePrevBottom > noteLastTop) {
notePrev.style.top = notes[i].offsetTop - marginNoteTop(notePrev) - notePrevHeight - newMargin + "px";
}
}
} /* end push up */
}
}
}/* end afterPageLayout*/
}
/* FUNCTIONS --------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------- */
//
function addCallAndMarker(section, classNotes, marker){
let notes = section.querySelectorAll(classNotes);
for (let i = 0; i < notes.length; ++i) {
let num = i + 1;
// Add call notes
var spanCall = document.createElement("span");
spanCall.classList.add("note-call");
spanCall.classList.add("note-call_" + classNotes);
spanCall.dataset.noteCall = classNotes + '-' + num;
spanCall.innerHTML = num;
notes[i].parentNode.insertBefore(spanCall, notes[i]);
// Add marker notes
var spanMarker = document.createElement("span");
spanMarker.classList.add("note-marker");
spanMarker.classList.add("note-marker_" + classNotes);
spanMarker.dataset.noteMarker = classNotes + '-' + num;
spanMarker.innerHTML = num + marker;
notes[i].prepend(spanMarker);
// Hide notes to avoid rendering problems
notes[i].style.display = "none";
}
}
// MARGINS
function marginNoteTop(elem) {
let marginTop = parseInt(window.getComputedStyle(elem).marginTop, 10)
return marginTop;
}
function marginNoteBottom(elem) {
let marginBottom = parseInt(window.getComputedStyle(elem).marginBottom, 10)
return marginBottom;
}
function biggestMargin(a, b) {
let margin;
let marginBottom = marginNoteBottom(a);
let marginTop = marginNoteTop(b);
if (marginBottom > marginTop) {
margin = marginBottom;
} else {
margin = marginTop;
}
return margin;
}
// ADD CSS
function addcss(css) {
var head = document.getElementsByTagName('head')[0];
var s = document.createElement('style');
s.setAttribute('type', 'text/css');
if (s.styleSheet) { // IE
s.styleSheet.cssText = css;
} else {// the world
s.appendChild(document.createTextNode(css));
}
head.appendChild(s);
}
// CAMEL CLASS NOTE
function toCamelClassNote(elem) {
let splitClass = elem.split("-");
if (splitClass.length > 1) {
for (let s = 1; s < splitClass.length; ++s) {
let strCapilize = splitClass[s].charAt(0).toUpperCase() + splitClass[s].slice(1)
splitClass[s] = strCapilize;
}
}
let reducer = (accumulator, currentValue) => accumulator + currentValue;
let classCamel = splitClass.reduce(reducer);
return classCamel;
}