Ignorer et passer au contenu
Les indispensables :
Plus de 10 000 écoles utilisent Bookinou pour créer des ateliers de lecture interactifs et autonomes . Donnez vie à tous les livres de votre classe, enregistrez vos propres contenus, et profitez de nos activités ludo-pédagogiques gratuites. 💡 Le paiement par mandat administratif est également possible.
Demander un devis Les différentes cartes Bookinou Vous êtes un peu perdus parmi les différentes cartes qui fonctionnent avec votre Bookinou? Pas de panique ! On vous explique tout :
La carte de nomade Quand votre enfant passe la carte de voyage sur son Bookinou, toutes les histoires enregistrées dans sa bibliothèque seront lues les unes après les autres.
Très pratique lorsque l’on part en voyage, pas besoin d’emmener tous ses livres !
Je découvre Les cartes à créer Vous pouvez associer vos différentes playlists à des cartes à créer. Vous pouvez même personnaliser vos cartes grâce à des stickers !
Vous pouvez par exemple créer une carte pour les trajets, une pour les histoires du soir... Vendu par 3.
Je découvre Les cartes à histoire Les cartes à histoires sont des cartes qui contiennent 4 à 10 histoires audio pré-enregistrées par des conteurs professionnels
Idéal pour découvrir de nouveaux éditeurs, héros ou thèmes avec son enfant
Je découvre Cet accessoire s'utilise avec Bookinou Cet accessoire ne peut pas être utilisées seul. Il fonctionne exclusivement avec le lecteur d’histoires Bookinou.
Vous n’avez pas encore de Bookinou ?
Découvrez dès maintenant notre lecteur d’histoires interactif, conçu pour stimuler l’amour de la lecture chez les enfants dès 2 ans.
Découvrir le lecteur Bookinou
Rechercher
\n sdfhgdf\n\n"}],"id":"bbced74f-6a9b-4b4c-a557-db58cc00e4d0","personalizeConfig":{"enabled":false,"previewMode":"overlay","baseImageUrl":"","baseImageAspectRatio":1,"useVariantImages":true,"layers":[],"export":{"enableDownloadButton":false,"enableCartAttachment":false,"downloadButtonText":"Download Preview","cartPropertyName":"Preview Image","exportFormat":"png","exportQuality":0.92}}};
window.EXPO.allOptionSets = [...window.EXPO.allOptionSets, expoTemp];
}
// Resolve the currently-selected variant id with fallback chain:
// 1) URL ?variant= param (set by theme on user interaction)
// 2) product form's [name="id"] input (kept in sync by theme JS)
// 3) server-rendered selected_or_first_available_variant id (initial auto-select)
window.EXPO.getCurrentVariantId = function() {
const urlVariantId = new URL(window.location.href).searchParams.get('variant');
if (urlVariantId) return urlVariantId;
const formIdInput = document.querySelector('form[action*="/cart/add"] [name="id"]');
const formIdValue = formIdInput && formIdInput.value && String(formIdInput.value).trim();
if (formIdValue) return formIdValue;
const defaultId = window.EXPO.currentProduct.selectedOrFirstAvailableVariantId;
return defaultId != null ? String(defaultId) : null;
};
// Function to get selected variant options.
// Returns null when the option values can't be determined — see the note in
// applyOptionSetsForCurrentContext on Liquid's 250-variant cap.
window.EXPO.getSelectedVariantOptions = function() {
const variantId = window.EXPO.getCurrentVariantId();
if (!variantId) return null;
const variant = window.EXPO.currentProduct.variants.find(
v => v.id.toString() === variantId || v.id === parseInt(variantId)
);
if (!variant) return null;
// Map variant options to { name, value } format
const variantOptions = [];
if (variant.option1) {
const option1Name = window.EXPO.currentProduct.options?.[0] ||
window.EXPO.currentProduct.productOptions?.[0]?.name;
if (option1Name) {
variantOptions.push({ name: option1Name, value: variant.option1 });
}
}
if (variant.option2) {
const option2Name = window.EXPO.currentProduct.options?.[1] ||
window.EXPO.currentProduct.productOptions?.[1]?.name;
if (option2Name) {
variantOptions.push({ name: option2Name, value: variant.option2 });
}
}
if (variant.option3) {
const option3Name = window.EXPO.currentProduct.options?.[2] ||
window.EXPO.currentProduct.productOptions?.[2]?.name;
if (option3Name) {
variantOptions.push({ name: option3Name, value: variant.option3 });
}
}
return variantOptions;
};
// Function to evaluate filters
window.EXPO.evaluateFilters = function(selectedVariantOptions) {
if (!window.EXPO.allOptionSets || window.EXPO.allOptionSets.length === 0) {
return [];
}
return window.EXPO.allOptionSets.filter(function(set){
if ( set.options.length < 1 ) {
return false;
}
var restriction = set.customerRestriction || "all";
if (restriction === "logged_in" && !window.EXPO.isLoggedIn) return false;
if (restriction === "guests" && window.EXPO.isLoggedIn) return false;
if ("all" === set.usingFilter) return true;
// Evaluate each filter condition
const evalFilter = (item) => {
const { property, comparation, value = [] } = item;
let result = value == window.EXPO.currentProduct[property];
// Tag filter
if (property === "tag") {
result = (window.EXPO.currentProduct.tags ?? []).some((tag) =>
value.includes(tag),
);
}
// Vendor filter
if (property === "vendor") {
if (Array.isArray(value)) {
result = value.includes(window.EXPO.currentProduct.vendor);
} else {
result = window.EXPO.currentProduct.vendor == value;
}
}
// Product type filter
if (property === "product_type") {
if (Array.isArray(value)) {
result = value.includes(window.EXPO.currentProduct.type);
} else {
result = window.EXPO.currentProduct.type == value;
}
}
// Product attribute filter (has option, any value)
if (property === "product_attribute") {
const attributeName = item.productAttributeName;
result = (window.EXPO.currentProduct.productOptions ?? []).some(
opt => opt.name === attributeName
);
}
// Variant option value filter (checks SELECTED VARIANT values only)
if (property === "variant_option") {
const optionName = item.variantOptionName;
const targetValues = Array.isArray(value) ? value : [value];
// Option values unknown (variant missing from the JSON): show the set
// rather than hide it, for both "is" and "is not".
if (!selectedVariantOptions) return true;
if (selectedVariantOptions.length === 0) {
result = false;
} else {
const selectedOption = selectedVariantOptions.find(
opt => opt.name === optionName
);
result = selectedOption ? targetValues.includes(selectedOption.value) : false;
}
}
return comparation === "is" ? result : !result;
};
// No filters defined — hide the set (avoids vacuous truth with .every())
if (!set.filters || set.filters.length === 0) return false;
const isIDFilter = set.filters.some(item => item.property === "id");
if ( isIDFilter ) {
return set.filters.some(evalFilter);
}
// AND logic: all filters must match. OR logic (default): any filter matches.
return set.filterLogic === "all"
? set.filters.every(evalFilter)
: set.filters.some(evalFilter);
});
};
// Apply option sets only when product and selected variant are available
window.EXPO.applyOptionSetsForCurrentContext = function() {
if (window.EXPO.currentProduct.available === false) {
window.EXPO.optionSets = [];
return;
}
const variantId = window.EXPO.getCurrentVariantId();
if (!variantId) {
window.EXPO.optionSets = [];
return;
}
const variant = window.EXPO.currentProduct.variants.find(
function(v) {
return v.id.toString() === variantId || v.id === parseInt(variantId, 10);
}
);
// A variant missing from currentProduct.variants is *unknown*, not sold out:
// Liquid caps product.variants at 250, so on products with more variants the
// selected one is often simply absent. Hide only when it is positively sold
// out, otherwise the whole block vanishes for every variant past the cap.
if (variant && variant.available === false) {
window.EXPO.optionSets = [];
return;
}
const selectedVariantOptions = window.EXPO.getSelectedVariantOptions();
window.EXPO.optionSets = window.EXPO.evaluateFilters(selectedVariantOptions);
};
window.EXPO.applyOptionSetsForCurrentContext();
// Listen for variant changes
window.EXPO.onVariantChange = function() {
window.EXPO.applyOptionSetsForCurrentContext();
const selectedVariantOptions = window.EXPO.getSelectedVariantOptions();
// Trigger custom event for React components to re-render
window.dispatchEvent(new CustomEvent('expo-variant-changed', {
detail: { selectedVariantOptions }
}));
};
// Watch for URL changes (variant selection)
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
window.EXPO.onVariantChange();
}
}).observe(document, {subtree: true, childList: true});
// Also listen for variant selector changes
document.addEventListener('change', function(e) {
if (e.target && (
e.target.name === 'id' ||
e.target.name === 'variant' ||
e.target.closest('variant-selects') ||
e.target.closest('[name="id"]')
)) {
setTimeout(() => {
window.EXPO.onVariantChange();
}, 100);
}
});