Pour tout problème contactez-nous par mail : support@froggit.fr | La FAQ :grey_question: | Rejoignez-nous sur le Chat :speech_balloon:

Skip to content
Snippets Groups Projects
main.js 31.5 KiB
Newer Older
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
module.exports = {
    build: {
Pierre Jarriges's avatar
Pierre Jarriges committed
        protected_dirs: ["assets", "style", "views", "standard"],
        default_meta_keys: ["title", "description", "image", "open_graph", "json_ld"],
    },
};

},{}],2:[function(require,module,exports){
module.exports = {
    images_url: `/assets/images`,
    data_url: `/assets/data`,
Pierre Jarriges's avatar
Pierre Jarriges committed
    translations_url: "/assets/translations"
Pierre Jarriges's avatar
Pierre Jarriges committed
},{}],3:[function(require,module,exports){
"use strict";
Pierre Jarriges's avatar
Pierre Jarriges committed
/**
 * A tiny library to handle text translation.
 */

/**
 * init parameter object type
 * @typedef TranslatorParam
 * @property {String[]} supported_languages - DEFAULT: ["en"] - An array of ISO 639 lowercase language codes
 * @property {String} locale ISO 639 lowercase language code - DEFAULT: "en"
 *
 * @property {Boolean} use_url_locale_fragment - DEFAULT: true -
 * If true, the 1st fragment of the window.location url will be parsed as a language code
 * Example:
 * window.location
 * > https://example.com/en/some-page...
 * then the /en/ part of the url will be taken in priority and locale will be set to "en".
 * window.location
 * > https://example.com/some-page...
 * No locale fragment will be found so locale will not be set from url fragment.
 * window.location
 * > https://example.com/some-page/en
 * Doesn't work, locale fragment must be the first framgment
 *
 * @property {String} local_storage_key  - DEFAULT: "translator-prefered-language"
 *  The key used to saved the current locale into local storage
 *
 * @property {String} translations_url - REQUIRED - the url of the directory containing the static json files for translations
 * Translations files are expected to be named with their corresponding locale code.
 * Example:
 * if supported_languages is set to ["en", "fr", "it"]
 * and translations_url is set to "https://example.com/translations/""
 * Then the expected translations files are
 * https://example.com/translations/en.json
 * https://example.com/translations/fr.json
 * https://example.com/translations/it.json
 * The json resources must simple key value maps, value being the translated text.
 */

module.exports = {
    locale: "en", // ISO 639 lowercase language code
    supported_languages: ["en"],
    translations: {},
    translations_url: "",
    use_url_locale_fragment: true,
    local_storage_key: "translator-prefered-language",

    /**
     * Initialize the lib with params
     * @param {TranslatorParam} params 
     * @returns {Promise}
     */
    init(params) {
        Object.entries(params).forEach(k_v => {
            const [key, value] = k_v;
            if ([
                "supported_languages",
                "use_url_locale_fragment",
                "local_storage_key",
                "translations_url"
            ].includes(key)) {
                this[key] = value;
            }
        });

        this.translations_url = this.format_translations_url(this.translations_url);
        this.supported_languages = this.format_supported_languages(this.supported_languages);

        return new Promise((resolve, reject) => {
            const loc =
                (() => {// Locale from url priority 1
                    if (this.use_url_locale_fragment) {
                        const first_url_fragment = window.location.pathname.substring(1).split("/")[0];
                        const fragment_is_locale = this.supported_languages.includes(first_url_fragment);
                        return fragment_is_locale ? first_url_fragment : "";
                    } else {
                        return "";
                    }
                })()
                || localStorage.getItem(this.local_storage_key) // Locale from storage priority 2
                || (() => { // locale from navigator priority 3
                    const navigator_locale = navigator.language.split("-")[0].toLocaleLowerCase();
                    return this.supported_languages.includes(navigator_locale)
                        ? navigator_locale
                        : this.supported_languages[0] // Default if navigator locale is not supported
                })();

            fetch(`${this.translations_url}${loc}.json`)
                .then(response => response.json())
                .then(response => {
                    this.locale = loc;
                    this.translations = response;
                    resolve();
                })
                .catch(err => {
                    this.locale = "en";
                    reject(err);
                });
        });
    },

    /**
     * Return a lowercase string without dash.
     * If given locale is en-EN, then "en" will be returned.
     * @param {String} locale 
     * @returns A lowercase string
     */
    format_locale(locale) {
        return locale.split("-")[0].toLowerCase();
    },

    /**
     * Appends a slash at the end of the given string if missing, and returns the url.
     * @param {String} url 
     * @returns {String}
     */
    format_translations_url(url) {
        if (url.charAt(url.length - 1) !== "/") {
            url += "/"
        }
        return url;
    },

    /**
     * Return the array of language codes formatted as lowsercase language code.
     * if ["en-EN", "it-IT"]is given, ["en", "it"] will b returned.
     * @param {String[]} languages_codes 
     * @returns {String[]}
     */
    format_supported_languages(languages_codes) {
        return languages_codes.map(lc => this.format_locale(lc));
    },

    /**
     * Fetches a new set of translations in case the wanted language changes
     * @param {String} locale A lowercase language code
     * @returns {Promise}
     */
    update_translations(locale) {
        locale = this.format_locale(locale);
        return new Promise((resolve, reject) => {
            fetch(`${this.translations_url}${locale}.json`)
                .then(response => response.json())
                .then(response => {
                    this.translations = response;
                    this.locale = locale;

                    localStorage.setItem(this.local_storage_key, locale);

                    const split_path = window.location.pathname.substring(1).split("/");
                    const first_url_fragment = split_path[0];
                    const fragment_is_locale = this.supported_languages.includes(first_url_fragment);

                    if (fragment_is_locale) {
                        split_path.splice(0, 1, locale);
                        const updated_path = split_path.join("/");
                        window.history.replaceState(null, "", "/" + updated_path);
                    }
                    resolve();
                })
                .catch(err => {
                    reject(err);
                });
        });
    },

    /**
     * Tries to get the translation of the source string, or return the string as it.
     * @param {String} text The source text to translate
     * @param {Object} params Some dynamic element to insert in the text
     * Params can be used if the translated text provide placeholder like {%some_word%}
     * Example:
     * translator.trad("Some trad key", {some_word: "a dynamic parameter"})
     * -> translation for "Some trad key": "A translated text with {%some_word%}"
     * -> will be return as : "A translated text with a dynamic parameter"
     * @returns {String}
     */
    trad: function (text, params = {}) {
        text = this.translations[text] || text;

        Object.keys(params).forEach(k => {
            text = text.replace(`{%${k}%}`, params[k]);
        });

        return text;
    }
};
},{}],4:[function(require,module,exports){
"use strict";

module.exports = {
Pierre Jarriges's avatar
Pierre Jarriges committed
    register_key: "objectToHtmlRender",

    /**
     * Register "this" as a window scope accessible variable named by the given key, or default.
     * @param {String} key 
     */
    register(key) {
        const register_key = key || this.register_key;
        window[register_key] = this;
    },

    /**
     * This must be called before any other method in order to initialize the lib.
     * It provides the root of the rendering cycle as a Javascript object.
     * @param {Object} renderCycleRoot A JS component with a render method.
     */
    setRenderCycleRoot(renderCycleRoot) {
        this.renderCycleRoot = renderCycleRoot;
    },

Pierre Jarriges's avatar
Pierre Jarriges committed
    event_name: "objtohtml-render-cycle",

    /**
     * Set a custom event name for the event that is trigger on render cycle.
     * Default is "objtohtml-render-cycle".
     * @param {String} evt_name 
     */
    setEventName(evt_name) {
        this.event_name = evt_name;
    },

    /**
     * This is the core agorithm that read an javascript Object and convert it into an HTML element.
     * @param {Object} obj The object representing the html element must be formatted like:
     * {
     *      tag: String // The name of the html tag, Any valid html tag should work. div, section, br, ul, li...
     *      xmlns: String // This can replace the tag key if the element is an element with a namespace URI, for example an <svg> tag.
     *                      See https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS for more information
     *      style_rules: Object // a object providing css attributes. The attributes names must be in JS syntax,
     *                              like maxHeight: "500px", backgrouncColor: "#ff2d56",  margin: 0,  etc.
     *      contents: Array or String // This reprensents the contents that will be nested in the created html element.
     *                                   <div>{contents}</div>
     *                                   The contents can be an array of other objects reprenting elements (with tag, contents, etc)
     *                                   or it can be a simple string.
     *      // All other attributes will be parsed as html attributes. They can be anything like onclick, href, onchange, title...
     *      // or they can also define custom html5 attributes, like data, my_custom_attr or anything.
     * }
     * @returns {HTMLElement} The output html node.
     */
    objectToHtml(obj) {
        if (!obj) return document.createElement("span"); // in case of invalid input, don't block the whole process.
        const objectToHtml = this.objectToHtml.bind(this);
        const { tag, xmlns } = obj;
        const node = xmlns !== undefined ? document.createElementNS(xmlns, tag) : document.createElement(tag);
        const excludeKeys = ["tag", "contents", "style_rules", "state", "xmlns"];

        Object.keys(obj)
            .filter(attr => !excludeKeys.includes(attr))
            .forEach(attr => {
Pierre Jarriges's avatar
Pierre Jarriges committed
                switch (attr) {
                    case "class":
                        node.classList.add(...obj[attr].split(" ").filter(s => s !== ""));
                        break;
                    case "on_render":
                        if (!obj.id) {
                            node.id = `${btoa(JSON.stringify(obj).slice(0, 127)).replace(/\=/g, '')}${window.performance.now()}`;
                        }
                        if (typeof obj.on_render !== "function") {
                            console.error("The on_render attribute must be a function")
                        } else {
                            this.attach_on_render_callback(node, obj.on_render);
                        }
                        break;
                    default:
                        if (xmlns !== undefined) {
                            node.setAttributeNS(null, attr, obj[attr])
                        } else {
                            node[attr] = obj[attr];
                        }
                }
            });
        if (obj.contents && typeof obj.contents === "string") {
            node.innerHTML = obj.contents;
        } else {
            obj.contents &&
                obj.contents.length > 0 &&
                obj.contents.forEach(el => {
                    switch (typeof el) {
                        case "string":
                            node.innerHTML = el;
                            break;
                        case "object":
Pierre Jarriges's avatar
Pierre Jarriges committed
                            if (xmlns !== undefined) {
                                el = Object.assign(el, { xmlns })
                            }
                            node.appendChild(objectToHtml(el));
                            break;
                    }
                });
        }

        if (obj.style_rules) {
            Object.keys(obj.style_rules).forEach(rule => {
                node.style[rule] = obj.style_rules[rule];
            });
        }

        return node;
    },
Pierre Jarriges's avatar
Pierre Jarriges committed

    on_render_callbacks: [],

    /**
     * This is called if the on_render attribute of a component is set.
     * @param {HTMLElement} node The created html element
     * @param {Function} callback The callback defined in the js component to render
     */
    attach_on_render_callback(node, callback) {
        const callback_handler = {
            callback: e => {
                if (e.detail.outputNode === node || e.detail.outputNode.querySelector(`#${node.id}`)) {
                    callback(node);
                    const handler_index = this.on_render_callbacks.indexOf((this.on_render_callbacks.find(cb => cb.node === node)));
                    if (handler_index === -1) {
                        console.warn("A callback was registered for node with id " + node.id + " but callbacck handler is undefined.")
                    } else {
                        window.removeEventListener(this.event_name, this.on_render_callbacks[handler_index].callback)
                        this.on_render_callbacks.splice(handler_index, 1);
                    }
                }
            },
            node,
        };

        const len = this.on_render_callbacks.push(callback_handler);
        window.addEventListener(this.event_name, this.on_render_callbacks[len - 1].callback);
    },

    /**
     * If a main element exists in the html document, it will be used as rendering root.
     * If not, it will be created and inserted.
     */
    renderCycle: function () {
Pierre Jarriges's avatar
Pierre Jarriges committed
        const main_elmt = document.getElementsByTagName("main")[0] || (function () {
            const created_main = document.createElement("main");
            document.body.appendChild(created_main);
            return created_main;
        })();

        this.subRender(this.renderCycleRoot.render(), main_elmt, { mode: "replace" });
Pierre Jarriges's avatar
Pierre Jarriges committed

    /**
     * This method behaves like the renderCycle() method, but rather that starting the rendering cycle from the root component,
    * it can start from any component of the tree. The root component must be given as the first argument, the second argument be
    * be a valid html element in the dom and will be used as the insertion target.
     * @param {Object} object An object providing a render method returning an object representation of the html to insert
     * @param {HTMLElement} htmlNode The htlm element to update
     * @param {Object} options can be used the define the insertion mode, default is set to "append" and can be set to "override",
         * "insert-before" (must be defined along with an insertIndex key (integer)),
         * "adjacent" (must be defined along with an insertLocation key (String)), "replace" or "remove".
         * In case of "remove", the first argument "object" is not used and can be set to null, undefined or {}...
     */
    subRender(object, htmlNode, options = { mode: "append" }) {
Pierre Jarriges's avatar
Pierre Jarriges committed
        let outputNode = null;

        const get_insert = () => {
            outputNode = this.objectToHtml(object);
            return outputNode;
        };

        switch (options.mode) {
            case "append":
Pierre Jarriges's avatar
Pierre Jarriges committed
                htmlNode.appendChild(get_insert());
                break;
            case "override":
                htmlNode.innerHTML = "";
Pierre Jarriges's avatar
Pierre Jarriges committed
                htmlNode.appendChild(get_insert());
                break;
            case "insert-before":
Pierre Jarriges's avatar
Pierre Jarriges committed
                htmlNode.insertBefore(get_insert(), htmlNode.childNodes[options.insertIndex]);
                break;
            case "adjacent":
                /**
                 * options.insertLocation must be one of:
                 *
                 * afterbegin
                 * afterend
                 * beforebegin
                 * beforeend
                 */
Pierre Jarriges's avatar
Pierre Jarriges committed
                htmlNode.insertAdjacentHTML(options.insertLocation, get_insert());
                break;
            case "replace":
Pierre Jarriges's avatar
Pierre Jarriges committed
                htmlNode.parentNode.replaceChild(get_insert(), htmlNode);
                break;
            case "remove":
                htmlNode.remove();
                break;
        }
Pierre Jarriges's avatar
Pierre Jarriges committed
        const evt_name = this.event_name;
        const event = new CustomEvent(evt_name, {
            detail: {
                inputObject: object,
                outputNode,
                insertOptions: options,
                targetNode: htmlNode,
            }
        });

        window.dispatchEvent(event);
Pierre Jarriges's avatar
Pierre Jarriges committed
},{}],5:[function(require,module,exports){
"use strict";

const { images_url } = require("../../constants");

class ThemeCard {
    constructor(props) {
        this.props = props;
    }

    render() {
        return {
            tag: "a",
            class: "theme-card",
            href: this.props.href,
            contents: [
                {
                    tag: "div",
                    class: "card-img",
                    contents: [{ tag: "img", alt: `thematic image ${this.props.img.replace(/\.[A-Za-z]+/, "")}`, src: `${images_url}/${this.props.img}` }],
                },
                {
                    tag: "div",
                    class: "card-title",
                    contents: [{ tag: "h2", class: "section-title", contents: this.props.title }],
                },
                {
                    tag: "div",
                    class: "card-description",
                    contents: [{ tag: "p", contents: this.props.description }],
                },
            ],
        };
    }
}

module.exports = ThemeCard;

Pierre Jarriges's avatar
Pierre Jarriges committed
},{"../../constants":2}],6:[function(require,module,exports){
"use strict";

const { images_url } = require("../constants");
const ThemeCard = require("./home-page-components/theme-card");
const WebPage = require("./lib/web-page");
Pierre Jarriges's avatar
Pierre Jarriges committed
const translator = require("ks-cheap-translator");
const t = translator.trad.bind(translator);

class HomePage extends WebPage {
Pierre Jarriges's avatar
Pierre Jarriges committed
    constructor() {
        super({ id: "home-page" });
    }

    render() {
        return {
            tag: "div",
Pierre Jarriges's avatar
Pierre Jarriges committed
            id: this.id,
            contents: [
                {
                    tag: "div",
                    class: "page-header",
                    contents: [
                        {
                            tag: "div",
                            class: "big-logo page-contents-center",
                            contents: [
                                {
                                    tag: "img",
                                    alt: "logo Kuadrado",
                                    src: `${images_url}/logo_kuadrado.svg`,
                                },
                                {
                                    tag: "img",
                                    class: "logo-text",
                                    alt: "Kuadrado",
                                    src: `${images_url}/logo_kuadrado_txt.svg`,
                                },
                            ],
                        },
                        { tag: "h1", contents: "Kuadrado Software", class: "page-contents-center" },
                        {
                            tag: "p",
                            class: "page-contents-center",
Pierre Jarriges's avatar
Pierre Jarriges committed
                            contents: t("kuadrado-home-description"),
                        },
                        {
                            tag: "ul",
                            class: "philo-bubbles",
Pierre Jarriges's avatar
Pierre Jarriges committed
                            contents: [t("Simplicité"), t("Légèreté"), t("Écologie")].map(word => {
                                return {
                                    tag: "li",
                                    contents: [{ tag: "span", contents: word }],
                                };
                            }),
                        },
                    ],
                },
                {
                    tag: "section",
                    class: "page-contents-center poles",
                    contents: [
                        {
Pierre Jarriges's avatar
Pierre Jarriges committed
                            title: t("Jeux"),
                            img: "game_controller.svg",
                            href: "/games/",
                            description:
Pierre Jarriges's avatar
Pierre Jarriges committed
                                t("games-description"),
Pierre Jarriges's avatar
Pierre Jarriges committed
                            title: t("Pédagogie"),
                            img: "brain.svg",
                            href: "/education/",
Pierre Jarriges's avatar
Pierre Jarriges committed
                            description: t("education-description"),
                        },
                        {
                            title: "Software",
                            img: "meca_proc.svg",
                            href: "/software-development/",
Pierre Jarriges's avatar
Pierre Jarriges committed
                            description: t("software-description"),
                        },
                    ].map(cardProps => new ThemeCard(cardProps).render()),
                },
            ],
        };
    }
}

module.exports = HomePage;

Pierre Jarriges's avatar
Pierre Jarriges committed
},{"../constants":2,"./home-page-components/theme-card":5,"./lib/web-page":7,"ks-cheap-translator":3}],7:[function(require,module,exports){
"use strict";
Pierre Jarriges's avatar
Pierre Jarriges committed
const translator = require("ks-cheap-translator");
const { translations_url } = require("../../constants");

class WebPage {
    constructor(args) {
        Object.assign(this, args);
Pierre Jarriges's avatar
Pierre Jarriges committed

        if (!this.id) {
            this.id = "webpage-" + performance.now();
        }

        translator.init({
            translations_url,
            supported_languages: ["fr", "en"],
        }).then(this.refresh_all.bind(this));
    }

    refresh() {
        obj2htm.subRender(this.render(), document.getElementById(this.id), { mode: "replace" })
    }

    refresh_all() {
        obj2htm.renderCycle()
    }
}

module.exports = WebPage;
Pierre Jarriges's avatar
Pierre Jarriges committed
},{"../../constants":2,"ks-cheap-translator":3}],8:[function(require,module,exports){
"use strict";

const HomePage = require("./homepage");
const runPage = require("./run-page");

runPage(HomePage);

Pierre Jarriges's avatar
Pierre Jarriges committed
},{"./homepage":6,"./run-page":9}],9:[function(require,module,exports){
"use strict";

Pierre Jarriges's avatar
Pierre Jarriges committed
const renderer = require("object-to-html-renderer")
const Template = require("./template/template");

module.exports = function runPage(PageComponent) {
    const template = new Template({ page: new PageComponent() });
Pierre Jarriges's avatar
Pierre Jarriges committed
    renderer.register("obj2htm")
    obj2htm.setRenderCycleRoot(template);
    obj2htm.renderCycle();
Pierre Jarriges's avatar
Pierre Jarriges committed
},{"./template/template":11,"object-to-html-renderer":4}],10:[function(require,module,exports){
"use strict";

const { images_url } = require("../../../constants");
Pierre Jarriges's avatar
Pierre Jarriges committed
const translator = require("ks-cheap-translator");
const t = translator.trad.bind(translator);

const NAV_MENU_ITEMS = [
    { url: "/games/", text: "Jeux" },
    {
        url: "/education/",
        text: "Pédagogie",
    },
    { url: "/software-development/", text: "Software" }
];

class NavBar {
    constructor() {
        this.initEventHandlers();
    }

    handleBurgerClick() {
        document.getElementById("nav-menu-list").classList.toggle("responsive-show");
    }

    initEventHandlers() {
        window.addEventListener("click", event => {
            if (
                event.target.id !== "nav-menu-list" &&
                !event.target.classList.contains("burger") &&
                !event.target.parentNode.classList.contains("burger")
            ) {
                document.getElementById("nav-menu-list").classList.remove("responsive-show");
            }
        });
    }

Pierre Jarriges's avatar
Pierre Jarriges committed
    handle_chang_lang(lang) {
        translator.update_translations(lang).then(() => {
            obj2htm.renderCycle();
        }).catch(err => console.log(err));
    }

    renderHome() {
        return {
            tag: "div",
            class: "home",
            contents: [
                {
                    tag: "a",
                    href: "/",
                    contents: [
                        {
                            tag: "img",
                            alt: "Logo Kuadrado",
                            src: `${images_url}/logo_kuadrado.svg`,
                        },
                        {
                            tag: "img",
                            alt: "Kuadrado Software",
                            class: "logo-text",
                            src: `${images_url}/logo_kuadrado_txt.svg`,
                        },
                    ],
                },
            ],
        };
    }

    renderMenu(menuItemsArray, isSubmenu = false, parentUrl = "") {
        return {
            tag: "ul",
            id: "nav-menu-list",
            class: isSubmenu ? "submenu" : "",
            contents: menuItemsArray.map(item => {
                const { url, text, submenu } = item;
                const href = `${parentUrl}${url}`;
                return {
                    tag: "li",
                    class: !isSubmenu && window.location.pathname === href ? "active" : "",
                    contents: [
                        {
                            tag: "a",
                            href,
Pierre Jarriges's avatar
Pierre Jarriges committed
                            contents: t(text),
                        },
                    ].concat(submenu ? [this.renderMenu(submenu, true, url)] : []),
                };
Pierre Jarriges's avatar
Pierre Jarriges committed
            }).concat({
                tag: "li",
                class: "lang-flags",
                contents: ["fr", "en"].map(lang => {
                    return {
                        tag: "img", src: `${images_url}/flag-${lang}.svg`,
                        class: translator.locale === lang ? "selected" : "",
                        onclick: this.handle_chang_lang.bind(this, lang)
                    }
                })
            }),
        };
    }

    renderResponsiveBurger() {
        return {
            tag: "div",
            class: "burger",
            onclick: this.handleBurgerClick.bind(this),
            contents: [{ tag: "span", contents: "···" }],
        };
    }

    render() {
        return {
            tag: "nav",
            contents: [
                this.renderHome(),
                this.renderResponsiveBurger(),
                this.renderMenu(NAV_MENU_ITEMS),
            ],
        };
    }
}

module.exports = NavBar;

Pierre Jarriges's avatar
Pierre Jarriges committed
},{"../../../constants":2,"ks-cheap-translator":3}],11:[function(require,module,exports){
"use strict";

const { in_construction } = require("../../config");
const { images_url } = require("../../constants");
const NavBar = require("./components/navbar");
Pierre Jarriges's avatar
Pierre Jarriges committed
const translator = require("ks-cheap-translator");
const t = translator.trad.bind(translator)

class Template {
    constructor(props) {
        this.props = props;
    }
    render() {
        return {
            tag: "main",
            contents: [
                {
                    tag: "header",
                    contents: [new NavBar().render()],
                },
                in_construction && {
                    tag: "section",
                    class: "warning-banner",
                    contents: [
                        {
                            tag: "strong",
                            class: "page-contents-center",
Pierre Jarriges's avatar
Pierre Jarriges committed
                            contents: t("Site en construction ..."),
                        },
                    ],
                },
                {
                    tag: "section",
                    id: "page-container",
                    contents: [this.props.page.render()],
                },
                {
                    tag: "footer",
                    contents: [
                        {
                            tag: "div",
                            class: "logo",
                            contents: [
                                {
                                    tag: "img",
                                    alt: `logo Kuadrado`,
                                    src: `${images_url}/logo_kuadrado.svg`,
                                },
                                {
                                    tag: "img",
                                    class: "text-logo",
                                    alt: "Kuadrado Software",
                                    src: `${images_url}/logo_kuadrado_txt.svg`,
                                },
                            ],
                        },
                        {
                            tag: "span",
                            contents: "32 rue Simon Vialet, 07240 Vernoux en Vivarais. Ardèche, France",
                        },
                        {
                            tag: "div",
                            contents: [
                                { tag: "strong", contents: "<blue>Contact : </blue>" },
                                {
                                    tag: "a",
                                    href: "mailto:contact@kuadrado-software.fr",
                                    contents: "contact@kuadrado-software.fr",
                                },
                            ],
                        },
                        {
                            tag: "div",
                            class: "social",
                            contents: [
                                {
                                    tag: "strong",
Pierre Jarriges's avatar
Pierre Jarriges committed
                                    contents: `<blue>${t("Sur les réseaux")} : </blue>`,
                                },
                                {
                                    tag: "a",
                                    href: "https://www.linkedin.com/company/kuadrado-software",
                                    target: "_blank",
                                    contents: "in",
                                    title: "Linkedin",
                                },
Pierre Jarriges's avatar
Pierre Jarriges committed
                                {
                                    tag: "a",
                                    href: "https://mastodon.gamedev.place/@kuadrado_software",
                                    target: "_blank",
                                    contents: "m",
                                    title: "Mastodon",
                                }
                            ],
                        },
                        {
                            tag: "span",
                            contents: `Copyleft 🄯 ${new Date()
                                .getFullYear()} Kuadrado Software | 
Pierre Jarriges's avatar
Pierre Jarriges committed
                                ${t("kuadrado-footer-copyleft")}`,
                        {
                            tag: "div", contents: [
Pierre Jarriges's avatar
Pierre Jarriges committed
                                { tag: "span", contents: t("Ce site web est") + " " },
                                {
                                    tag: "a", target: "_blank",
                                    style_rules: { fontWeight: "bold" },
                                    href: "https://gitlab.com/kuadrado-software/kuadrado-website/-/blob/master/README.md",
                                    contents: "OPEN SOURCE"
                                }
                            ]
                        }
                    ],
                },
            ],
        };
    }
}

module.exports = Template;

Pierre Jarriges's avatar
Pierre Jarriges committed
},{"../../config":1,"../../constants":2,"./components/navbar":10,"ks-cheap-translator":3}]},{},[8]);