//TODO: repasar y quitar lo relacionado con selectores y precio. /*TODO: -Se llama a la [async function init()] dentro de aqui [ecomNormaReferencedScript] desde fragment [dxp-ecom-portal/misc/fragments/Collection Tienda/ECOM-Global_functions/index.js] de la siguiente manera: if (typeof window.ecomNormaReferencedScript?.init === "function") { await window.ecomNormaReferencedScript.init(); } */ window.ecomNormaReferencedScript = (function ($) { /* === CONFIG === */ let DEBUG = false; let ROOT_SELECTOR = null; function log(...args) { if (DEBUG) console.log("[ecomNorma]", ...args); } /* === FUNCIONES PRIVADAS === */ function _euros(v) { const n = Number(v) || 0; return n.toLocaleString("es-ES", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + " €"; } function _uniq(a) { return Array.from(new Set(a)); } function _fill($sel, values, textFn) { const keep = $sel.val(); $sel.empty(); values.forEach(v => { $sel.append($("<option>", { value: v, text: textFn ? textFn(v) : v })); }); if (keep && values.includes(keep)) $sel.val(keep); else if (values.length) $sel.val(values[0]); } function _relabel($sel, textFn) { $sel.find("option").each(function () { const v = $(this).attr("value"); $(this).text(textFn ? textFn(v) : v); }); } /* === initCarousel — PÚBLICA === */ function initCarousel(force = false) { const $owl = $(ROOT_SELECTOR + " .standards-container"); // no inicializar si no existe, está oculto o no tiene items if (!$owl.length || !$owl.hasClass("owl-carousel") || !$owl.is(':visible') || $owl.children().length === 0) { log("initCarousel abortado: no visible o sin items", $owl); return; } if (force && $owl.hasClass("owl-loaded") && $owl.data("owl.carousel")) { log("initCarousel Destruyendo Owl Carousel existent:", $owl); $owl.trigger('destroy.owl.carousel'); // limpiar también owl-hidden para permitir reinicialización $owl.removeClass('owl-loaded owl-hidden'); // limpiar markup de Owl $owl.find('.owl-stage-outer').children().unwrap(); } if ($owl.length && !$owl.hasClass("owl-loaded") && !$owl.data("owl.carousel")) { $owl.owlCarousel({ nav: true, navText: [ '<i class="fa-solid fa-chevron-left"></i>', '<i class="fa-solid fa-chevron-right"></i>' ], loop: false, dots: false, pagination: false, margin: 25, autoHeight: false, responsive: { 0: { items: 1 }, 500: { items: 1 }, 767: { items: 2 }, 1000: { items: 3 }, 1200: { items: 4 } } }); $owl.off("initialized.owl.carousel refreshed.owl.carousel resized.owl.carousel translated.owl.carousel") .on("initialized.owl.carousel refreshed.owl.carousel resized.owl.carousel translated.owl.carousel", adjustHeights); log("initCarousel executed"); } } /* === initCard — PÚBLICA === */ function initCard($card) { const dataNode = $card.find("script.normas-data")[0]; let data = []; if (dataNode) { try { data = JSON.parse(dataNode.textContent); } catch {} } const hasData = Array.isArray(data) && data.length > 0; let langMap = {}; const mapNode = $card.find("script.lang-map")[0]; try { if (mapNode) langMap = JSON.parse(mapNode.textContent); } catch {} const $lang = $card.find(".select-language"); const $fmt = $card.find(".select-format"); const $price = $card.find(".price-ae"); const toUC = s => (s || "").toString().toUpperCase(); const labelOf = lang => langMap[toUC(lang)] || lang; const formatsFor = lang => _uniq(data.filter(d => d.language === lang).map(d => d.format)); const languagesFor = fmt => _uniq(data.filter(d => d.format === fmt).map(d => d.language)); const priceOf = (lang, fmt) => { const m = data.find(d => d.language === lang && d.format === fmt); return m ? Number(m.price) || 0 : 0; }; const parsePriceText = (text) => { if (!text) return 0; const clean = String(text).replace(/[^\d,.-]/g, "").trim(); if (!clean) return 0; if (clean.includes(",") && clean.includes(".")) { const normalized = clean.replace(/\./g, "").replace(",", "."); return Number(normalized) || 0; } const normalized = clean.replace(",", "."); return Number(normalized) || 0; }; if (hasData) { function onLanguageChange() { const lang = $lang.val(); const fmts = formatsFor(lang); _fill($fmt, fmts); $price.text(_euros(priceOf(lang, $fmt.val()))); } function onFormatChange() { const fmt = $fmt.val(); const langs = languagesFor(fmt); _fill($lang, langs, labelOf); $price.text(_euros(priceOf($lang.val(), fmt))); } const l0 = $lang.val(); const f0 = $fmt.val(); if (!data.find(d => d.language === l0 && d.format === f0)) onLanguageChange(); else $price.text(_euros(priceOf(l0, f0))); _relabel($lang, labelOf); $lang.off("change.ae").on("change.ae", onLanguageChange); $fmt.off("change.ae").on("change.ae", onFormatChange); } async function onAdd(e) { e.preventDefault(); var entryType = ($card.data('entrytype') || 'Norma'); var code = ($card.data('code') || '').toString(); var codIdioma = ($card.find('.select-language').val() || '').toString(); var codFormato = ($card.find('.select-format').val() || '').toString(); if (!code) code = ($card.find('.title-standard').text() || '').trim(); await window.ecomGlobalScripts.functions.addToCart({ entryType: entryType, code: code, codIdioma: codIdioma, codFormato: codFormato, price: Number(priceOf(codIdioma, codFormato)) || 0, name: ($card.data('title') || '').toString(), amount: 1 }); } $card.find(".standard-button").off("click.ae").on("click.ae", function(e){ const btn = this; const run = () => onAdd(e); const helper = window.ecomGlobalScripts?.functions?.withButtonSpinner; if (typeof helper === "function") { return helper(btn, run, { spinnerClass: "spinner-border spinner-border-sm text-light", mode: "replace" }); } return run(); }); // Evita que Owl Carousel interprete el click como drag horizontal $card.find(".standard-button") .off("pointerdown.owl mousedown.owl touchstart.owl") .on("pointerdown.owl mousedown.owl touchstart.owl", function(e){ e.stopPropagation(); }); } /* === adjustHeights — PÚBLICA === */ function adjustHeights() { if ($(window).width() > 766.98) { let hT = 0, hD = 0; $(ROOT_SELECTOR + " .title-standard").css("height","auto").each(function(){ hT=Math.max(hT,$(this).outerHeight()); }); $(ROOT_SELECTOR + " .description-text").css("height","auto").each(function(){ hD=Math.max(hD,$(this).outerHeight()); }); $(ROOT_SELECTOR + " .title-standard").height(hT); $(ROOT_SELECTOR + " .description-text").height(hD); } else { $(ROOT_SELECTOR + " .title-standard, .description-text").css("height","auto"); } log("adjustHeights executed"); } /* === init — PÚBLICA === */ async function init() { // debug externo DEBUG = window.ecomNormaScripts?.properties?.isDebug === true; // prefijo para todos los selectores ROOT_SELECTOR = window.ecomGlobalScripts?.properties?.querySelectors?.rootSelector || window.ecomNormaScripts?.properties?.querySelectors?.rootSelector || "#ecom-norma-detail-normas-referenced .standards-section"; log("window.ecomNormaReferencedScript init"); $(ROOT_SELECTOR + " .item-standard").each(function(){ initCard($(this)); }); initCarousel(true); adjustHeights(); $(window).off("resize.standards").on("resize.standards", adjustHeights); log("init executed"); } /* === API pública === */ return { init, initCard, adjustHeights, initCarousel }; })(jQuery); window.ecomGenericScripts = window.ecomGenericScripts || (function () { /* ===================================== PROPIEDADES PRIVADAS ===================================== */ const privateProps = window.ecomGenericScripts?.privateProps || {}; /* ===================================== PROPIEDADES PÚBLICAS ===================================== */ const publicProps = { isDebug: false }; /* ===================================== FUNCIONES PÚBLICAS ===================================== */ //En functions declaramos la functions publicas const functions = {}; functions.init = async function () { await _DOMContentLoaded(); }; /* ===================================== FUNCIONES PRIVADAS ===================================== */ const _loadInit = async function () { if (publicProps.isDebug) console.log("DOMContentLoaded ecom Generic scripts"); }; const _parsePriceText = function (text) { if (!text) return 0; const cleaned = String(text) .replace(/[^\d,.-]/g, "") .replace(/\./g, "") .replace(",", "."); const n = Number(cleaned); return Number.isFinite(n) ? n : 0; }; const _getDetailContext = function (button) { const wrapper = document.querySelector("#ecom-scripts .data-product-wrapper"); const purchaseBox = button?.closest(".purchase-box") || document; const langSelect = purchaseBox.querySelector(".select-language"); const formatSelect = purchaseBox.querySelector(".select-format"); const qtyInput = purchaseBox.querySelector(".qty input[type='number']"); const priceNode = purchaseBox.querySelector(".price-header .price"); const titleNode = document.querySelector(".ecom-libro .title, .ecom-norma .title, .ecom-coleccion_tematica .title, .title-book"); return { wrapper, purchaseBox, langSelect, formatSelect, qtyInput, priceNode, titleNode }; }; const _getDetailData = async function (productERC, productId, isLibro, isNorma, isColeccionTematica) { if (!window.ecomGlobalScripts?.functions) return null; try { if (isLibro) { if (productERC) return await window.ecomGlobalScripts.functions.getProductBooksByERC(productERC); if (productId) return await window.ecomGlobalScripts.functions.getProductBooks(productId); } if (isColeccionTematica) { if (productERC) return await window.ecomGlobalScripts.functions.getProductThematicCollectionsByERC(productERC); if (productId) return await window.ecomGlobalScripts.functions.getProductThematicCollections(productId); } if (isNorma) { if (productERC) return await window.ecomGlobalScripts.functions.getProductNormasDetailsByERC(productERC); if (productId) return await window.ecomGlobalScripts.functions.getProductNormasDetails(productId); } } catch (e) {} return null; }; const _syncDetailBuyButtonState = function (purchaseBox) { if (!purchaseBox) return; const btn = purchaseBox.querySelector(".buy-btn"); if (!btn) return; const langSelect = purchaseBox.querySelector(".select-language"); const formatSelect = purchaseBox.querySelector(".select-format"); const hasSelectors = !!(langSelect || formatSelect); const hasSelection = !!(langSelect?.value && formatSelect?.value); if (!hasSelectors) { if (!btn.disabled) btn.disabled = true; if (!btn.classList.contains("disabled")) btn.classList.add("disabled"); return; } if (btn.disabled === hasSelection) { btn.disabled = !hasSelection; } btn.classList.toggle("disabled", !hasSelection); }; const _setupQtyPriceMultiplier = function (purchaseBox) { if (!purchaseBox || purchaseBox.dataset.aeQtyMultBound === "true") return; purchaseBox.dataset.aeQtyMultBound = "true"; const priceNode = purchaseBox.querySelector(".price-header .price"); const qtyInput = purchaseBox.querySelector(".qty input[type='number']"); if (!priceNode || !qtyInput) return; const formatter = window.ecomGlobalScripts?.properties?.language?.formatterPrice; const splitPriceAndCurrency = function (text) { const m = String(text || "").match(/^(.*?)(\s*[^\d.,\s]+)\s*$/); if (m) return [m[1].trim(), m[2].trim()]; return [String(text || "").trim(), ""]; }; const formatPrice = function (value, currency) { const numText = formatter ? formatter.format(value) : value.toLocaleString("es-ES", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); return currency ? numText + " " + currency : numText; }; const captureUnit = function () { const parts = splitPriceAndCurrency(priceNode.textContent); const parsed = _parsePriceText(parts[0]); if (parsed > 0) { purchaseBox.dataset.aeUnitPrice = String(parsed); purchaseBox.dataset.aeUnitCurrency = parts[1]; } }; let observer; const startObserve = function () { observer.observe(priceNode, { childList: true, characterData: true, subtree: true }); }; const reapply = function () { const unit = Number(purchaseBox.dataset.aeUnitPrice) || 0; if (!unit) return; const qty = Math.max(1, parseInt(qtyInput.value, 10) || 1); const total = unit * qty; const currency = purchaseBox.dataset.aeUnitCurrency || ""; if (observer) observer.disconnect(); priceNode.textContent = formatPrice(total, currency); if (observer) startObserve(); }; // Cuando _renderFragmentPrice (en ECOM-Global_functions/index.js) reescribe // .price con el precio unitario tras cambiar idioma/formato, recapturamos // el unitario y reaplicamos la cantidad actual. observer = new MutationObserver(function () { captureUnit(); reapply(); }); startObserve(); qtyInput.addEventListener("input", reapply); qtyInput.addEventListener("change", reapply); // Captura inicial por si el precio ya estaba renderizado antes de montar el observer. captureUnit(); reapply(); }; const _bindDetailSelectorState = function (purchaseBox) { if (!purchaseBox) return; const sync = function () { _syncDetailBuyButtonState(purchaseBox); }; if (purchaseBox.dataset.aeSelectorsBound === "true") { sync(); return; } purchaseBox.dataset.aeSelectorsBound = "true"; purchaseBox.addEventListener("change", function (event) { if (!event.target?.matches(".select-language, .select-format")) return; sync(); }); const selectorsRoot = purchaseBox.querySelector(".selector-language_format") || purchaseBox; const observer = new MutationObserver(function () { sync(); }); observer.observe(selectorsRoot, { childList: true, subtree: true }); sync(); }; const _attachDetailAddToCart = async function () { const buttons = document.querySelectorAll(".purchase-box .buy-btn"); if (!buttons.length) return; buttons.forEach(btn => { _bindDetailSelectorState(btn.closest(".purchase-box")); _setupQtyPriceMultiplier(btn.closest(".purchase-box")); if (btn.dataset.aeBound === "true") return; btn.dataset.aeBound = "true"; btn.addEventListener("click", (e) => { e.preventDefault(); const run = async () => { const ctx = _getDetailContext(btn); const wrapper = ctx.wrapper; if (!wrapper) { window.dispatchEvent(new CustomEvent("cart:error", { detail: { message: "Error al añadir el producto a la cesta" } })); return; } const productId = Number(wrapper.dataset.productId) || null; const productERC = wrapper.dataset.productErc || ""; const isLibro = wrapper.dataset.productIslibro === "true"; const isNorma = wrapper.dataset.productIsnorma === "true"; const isColeccionTematica = wrapper.dataset.productIscolecciontematica === "true"; const codIdioma = (ctx.langSelect?.value || "").toString(); const codFormato = (ctx.formatSelect?.value || "").toString(); if ((ctx.langSelect || ctx.formatSelect) && (!codIdioma || !codFormato)) { window.dispatchEvent(new CustomEvent("cart:error", { detail: { message: "Selecciona idioma y formato" } })); return; } const entryType = isLibro ? "Libro" : isColeccionTematica ? "Colección Temática" : "Norma"; const code = productERC || (ctx.titleNode?.textContent || "").trim(); const qty = Math.max(1, parseInt(ctx.qtyInput?.value, 10) || 1); const name = (ctx.titleNode?.textContent || "").trim(); let priceToSend = 0; const data = await _getDetailData(productERC, productId, isLibro, isNorma, isColeccionTematica); if (Array.isArray(data) && data.length && codIdioma && codFormato) { const entry = data.find(item => { const langKey = item.codLanguage ?? item.language; const fmtKey = item.codFormat ?? item.format; return String(langKey) === codIdioma && String(fmtKey) === codFormato; }); if (entry) { const basePrice = Number(entry.price) || 0; const discount = Number(entry.webDiscount) || 0; priceToSend = discount > 0 ? Math.floor((basePrice - (basePrice * discount / 100)) * 100) / 100 : basePrice; } } if (!priceToSend && ctx.priceNode) { priceToSend = _parsePriceText(ctx.priceNode.textContent); } await window.ecomGlobalScripts.functions.addToCart({ entryType: entryType, code: code, codIdioma: codIdioma, codFormato: codFormato, price: priceToSend, name: name, amount: qty }); }; const helper = window.ecomGlobalScripts?.functions?.withButtonSpinner; if (typeof helper === "function") { return helper(btn, run, { spinnerClass: "spinner-border spinner-border-sm text-light", mode: "replace" }); } return run(); }); }); }; const _DOMContentLoaded = async function() { //Event DOMContentLoaded if (publicProps.isDebug) console.log("DOMContentLoaded ecom Generic scripts"); if (publicProps.isDebug) console.log("DOMContentLoaded ecom Generic scripts - _loadInit execute"); await _loadInit(); await _attachDetailAddToCart(); }; // ---- Execute Listener DOMContentLoaded ---- document.addEventListener("DOMContentLoaded", async function() { await _DOMContentLoaded(); }); /* ===================================== API PÚBLICA ===================================== */ return { properties: publicProps, // properties public functions: functions // functions public }; })();
-50% de descuento* Si compras la misma norma UNE en distintos idiomas. * Dto. sobre el pvp inferior. Ver condiciones

ISO/IEC 30134-1:2016

Information technology — Data centres — Key performance indicators — Part 1: Overview and general requirements

Fecha edición: 2016-03-21
En Vigor
Idiomas disponibles: Inglés
ICS: 35.020 - Tecnología de la información (TI) en general
CTN: 654019

Modificaciones Normas

Es modificada por ISO/IEC 30134-1:2016/Amd 1:2018

Normas Conjuntas

Trabajo conjunto ISO/IEC 30134-1:2016

El libro en palabras del autor

Ultricies magna feugiat malesuada sociosqu varius vivamus cubilia parturient, himenaeos vitae vehicula nam placerat netus urna platea, nostra rutrum felis mattis penatibus velit quisque.

Button
Preguntas frecuentes ¿Tienes alguna duda sobre nuestros productos?

Respuesta 2

Desde la web

Libros y normas