257 lines
7.8 KiB
JavaScript
257 lines
7.8 KiB
JavaScript
// import { createApp } from 'vue'
|
||
import '../scss/main.scss'
|
||
|
||
import "splitting/dist/splitting.css";
|
||
import "splitting/dist/splitting-cells.css";
|
||
import Splitting from "splitting";
|
||
|
||
// import Content from './vuejs/Content.vue'
|
||
|
||
// import REST from './api/rest-axios'
|
||
|
||
// import LocomotiveScroll from 'locomotive-scroll';
|
||
// const scroll = new LocomotiveScroll();
|
||
|
||
|
||
// /**
|
||
// * @file
|
||
// * https://www.drupal.org/docs/drupal-apis/javascript-api/javascript-api-overview
|
||
// */
|
||
// (function (Drupal) {
|
||
|
||
// 'use strict';
|
||
|
||
// Drupal.behaviors.leshed = {
|
||
// attach: function (context, settings) {
|
||
// console.log('It works!');
|
||
// }
|
||
// };
|
||
|
||
// } (Drupal));
|
||
|
||
(function (Drupal, drupalSettings) {
|
||
const LeShedTheme = function () {
|
||
const _is_front = drupalSettings.path.isFront
|
||
console.log('drupalSettings', drupalSettings)
|
||
|
||
|
||
// ___ _ _
|
||
// |_ _|_ _ (_) |_
|
||
// | || ' \| | _|
|
||
// |___|_||_|_|\__|
|
||
function init () {
|
||
console.log('LeShedTheme init()')
|
||
initTitles();
|
||
initBurgerMenu();
|
||
// initVues()
|
||
}
|
||
|
||
|
||
function onWindowResize() {
|
||
_cardsForms.forEach(({card, forme, formepos}) => {
|
||
// console.log(`card`, card, 'forme', forme, 'formepos', formepos);
|
||
forme.style.top = `${card.offsetTop + formepos.top}px`;
|
||
forme.style.left = `${card.offsetLeft + formepos.left}px`;
|
||
})
|
||
}
|
||
|
||
function randomInt(min, max) {
|
||
return Math.floor(Math.random() * (max - min)) + min;
|
||
}
|
||
|
||
// Rendu typographique des titres. Police Epilogue = axe variable `wght`
|
||
// (100–900) uniquement ; l'italique est un fichier séparé (font-style).
|
||
const TITLE_STYLE = {
|
||
// 3 groupes de graisse : thin = majorité (défaut), medium = quelques,
|
||
// bold = les pics. Les fines ne sont pas comptées (c'est le reste).
|
||
wghtThin: { min: 100, max: 200 }, // majorité (défaut)
|
||
wghtMedium: { min: 200, max: 400 }, // quelques
|
||
wghtBold: { min: 800, max: 900 }, // pics
|
||
boldRatio: 0.28, // proportion de pics gras par mot
|
||
mediumRatio: 0.12, // proportion de medium par mot
|
||
italicOnBold: 0.6, // proba d'italique sur un pic
|
||
italicOnMedium: 0.4, // proba d'italique sur un medium
|
||
italicOnThin: 0.30, // proba d'italique sur une fine
|
||
};
|
||
|
||
function initTitles() {
|
||
const titles = document.querySelectorAll('#block-leshed-identitedusite>a, article.node-type-projet>header>h2');
|
||
for (const txt of titles) {
|
||
const splitted = Splitting({ target: txt, by: 'chars' });
|
||
for (const word of splitted[0].words) {
|
||
styleWordChars(Array.from(word.getElementsByClassName('char')));
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Tire k positions réparties régulièrement dans [0, n[ : le mot est découpé
|
||
* en k cases contiguës, une position au hasard par case (aléa contrôlé, pas
|
||
* de paquets). Les positions déjà prises par un autre groupe sont évitées.
|
||
*/
|
||
function spreadPositions(n, k, exclude = new Set()) {
|
||
const set = new Set();
|
||
for (let i = 0; i < k; i++) {
|
||
const start = Math.floor((i * n) / k);
|
||
const end = Math.floor(((i + 1) * n) / k); // borne exclue
|
||
const span = Math.max(1, end - start);
|
||
let pos = randomInt(start, start + span);
|
||
let guard = 0;
|
||
while ((exclude.has(pos) || set.has(pos)) && guard < span) {
|
||
pos = start + ((pos - start + 1) % span);
|
||
guard++;
|
||
}
|
||
set.add(pos);
|
||
}
|
||
return set;
|
||
}
|
||
|
||
/**
|
||
* Applique graisse + italique caractère par caractère.
|
||
*
|
||
* Majorité de fines ; quelques medium et quelques pics gras, chacun réparti
|
||
* régulièrement dans le mot. L'italique est corrélé à la graisse.
|
||
*/
|
||
function styleWordChars(chars) {
|
||
const n = chars.length;
|
||
if (!n) return;
|
||
|
||
// Accents répartis régulièrement : d'abord les pics gras, puis les medium
|
||
// (en évitant les positions déjà grasses). Le reste sera fin.
|
||
const bold = spreadPositions(n, Math.round(n * TITLE_STYLE.boldRatio));
|
||
const medium = spreadPositions(n, Math.round(n * TITLE_STYLE.mediumRatio), bold);
|
||
|
||
// 1) Décide graisse + italique pour chaque caractère.
|
||
const decisions = chars.map((char, i) => {
|
||
let range, pItal;
|
||
if (bold.has(i)) {
|
||
range = TITLE_STYLE.wghtBold;
|
||
pItal = TITLE_STYLE.italicOnBold;
|
||
}
|
||
else if (medium.has(i)) {
|
||
range = TITLE_STYLE.wghtMedium;
|
||
pItal = TITLE_STYLE.italicOnMedium;
|
||
}
|
||
else {
|
||
range = TITLE_STYLE.wghtThin;
|
||
pItal = TITLE_STYLE.italicOnThin;
|
||
}
|
||
return {
|
||
char,
|
||
wght: randomInt(range.min, range.max + 1),
|
||
ital: Math.random() < pItal,
|
||
};
|
||
});
|
||
|
||
// 2) Au moins une italique par mot : si aucune, on en force une, de
|
||
// préférence sur un pic gras ou un medium, sinon sur une lettre au hasard.
|
||
if (!decisions.some((d) => d.ital)) {
|
||
const accents = [...bold, ...medium];
|
||
const idx = accents.length ? accents[randomInt(0, accents.length)] : randomInt(0, n);
|
||
decisions[idx].ital = true;
|
||
}
|
||
|
||
// 3) Pas plus de 2 italiques consécutives : on casse les séries de 3+.
|
||
let run = 0;
|
||
for (const d of decisions) {
|
||
if (d.ital) {
|
||
run += 1;
|
||
if (run > 2) {
|
||
d.ital = false;
|
||
run = 0;
|
||
}
|
||
}
|
||
else {
|
||
run = 0;
|
||
}
|
||
}
|
||
|
||
// 4) Applique.
|
||
for (const { char, wght, ital } of decisions) {
|
||
char.style.fontVariationSettings = `'wght' ${wght}`;
|
||
char.style.fontStyle = ital ? 'italic' : 'normal';
|
||
char.classList.toggle('italic', ital);
|
||
char.classList.toggle('normal', !ital);
|
||
}
|
||
}
|
||
|
||
function initBurgerMenu() {
|
||
let header_right = document.getElementById('burger-btn');
|
||
header_right.parentElement.addEventListener('click', function(e){
|
||
// console.log('click header_right', this);
|
||
this.firstElementChild.toggleAttribute('opened');
|
||
this.firstElementChild.nextElementSibling.toggleAttribute('opened');
|
||
})
|
||
}
|
||
|
||
// function initVues(){
|
||
// console.log('initVues');
|
||
|
||
// // initVueContent();
|
||
|
||
|
||
// }
|
||
|
||
// function initVueContent(){
|
||
// createApp(Content).mount('main[role="main"]');
|
||
|
||
// // processEtapeLinks();
|
||
// }
|
||
|
||
|
||
// function onClickEtapeLink(e){
|
||
// e.preventDefault();
|
||
|
||
// let a = e.currentTarget;
|
||
// let nid = a.dataset.nodeNid;
|
||
// console.log(nid);
|
||
|
||
// getNodeData(nid);
|
||
|
||
// return null;
|
||
// }
|
||
|
||
|
||
// function processEtapeLinks(){
|
||
// let etape_link_fields = document.querySelectorAll('#etapes-liste div.views-field-title');
|
||
// etape_link_fields.forEach((field, index) => {
|
||
// let nid = null;
|
||
// let classList = field.classList;
|
||
// classList.forEach((classe) => {
|
||
// let reg = /data-node-(\d+)/;
|
||
// let result = classe.match(reg);
|
||
// if (result) {
|
||
// nid = result[1];
|
||
// console.log(nid);
|
||
// }
|
||
// })
|
||
|
||
// if (nid) {
|
||
// let a = field.querySelector('a');
|
||
// a.setAttribute('data-node-nid', nid);
|
||
// a.addEventListener('click', onClickEtapeLink);
|
||
// }
|
||
|
||
|
||
// })
|
||
// }
|
||
|
||
// function getNodeData(nid){
|
||
// const params = {
|
||
// }
|
||
// REST.get(`/node/${nid}?_format=json`, params)
|
||
// .then((data) => {
|
||
// console.log('user REST getUser data', data)
|
||
// })
|
||
// .catch(error => {
|
||
// console.warn('Issue with getNodedata', error)
|
||
// Promise.reject(error)
|
||
// })
|
||
// }
|
||
|
||
init()
|
||
} // end LeShedTheme()
|
||
|
||
LeShedTheme()
|
||
})(Drupal, drupalSettings)
|