Lightbox Responsive et A11Y

    Principe de base d'une lightbox accessible, compatible lecteurs d'écran et responsive. Les attributs srcset peuvent être pris en charge.
    Toute proposition d'amélioration est bien sûr la bienvenue ! Ça se passe ici : contact

    Les ressources

    Utilisation

    Tout est dans le js... Exemple ici

    ou sur l'image suivante
    image Berlin-Potsdamer-Platz
    <img decoding="async"
        class="lightbox lightbox-overlay-trigger lazy-io"
        title="Berlin, Potsdamer Platz"
        data-link="https://www.kortic.com/photo-berlin-potsdamer-platz.html"
        data-full="https://www.kortic.com/images/full/2000/1/berlin-potsdamer-platz.jpg"
        data-enable-background="https://www.kortic.com/images/blur/1200/0/berlin-potsdamer-platz.jpg"
        alt="image Berlin-Potsdamer-Platz"
        src="data:image/gif;base64,R0lGODlhCgAKAIABAAAAAP///yH5BAEAAAEALAAAAAAKAAoAAAIIjI+py+0PYysAOw=="
        data-srcset="https://www.kortic.com/images/card/400/0/berlin-potsdamer-platz.jpg"
        />

    Source Javascript $.lightbox

    ;(async ($) => {

        /*     utilisation
            usage sur n'importe quelle balise HTML de la catégorie contenu de flux (https://developer.mozilla.org/fr/docs/Web/Guide/HTML/Catégories_de_contenu#Contenu_de_flux)

            Exemple:
            <picture class="lightbox" data-full="//domaine.com/src_image.jpg" title="Lorem ipsum" ou data-title="Lorem ipsum">

            Valeurs obligatoires :
            - classe `lightbox`
            - attribut data-full (par défaut) modifiable pour gérer plusieurs lightbox dans une même page.
              La valeur de data-full est la source de l'image en grand format affiché dans la lightbox.
              [data-full] : string uri OU objet pour images responsives. eg. {"src":"\/image@1000.jpg","srcset":["\/image@400.jpg 400vw","\/image@700.jpg 700vw","\/image@1500.jpg 1500vw"]}
            - [data-]title non vide


            Valeurs optionnelles :
            - classe `lightbox-overlay-trigger` : pose la zone cliquable sur la totalité du bloc. Si absent, le trigger cliquable se positionne en haut à droite du bloc
            - data-download : définit le lien dans la lightbox vers le téléchargement direct de l'image courante
            - data-link : définit le lien dans la lightbox vers l'affichage d'une page détaillant l'image courante
            - data-scroll : contient un identifiant qui permet de faire suivre la page au changement de vue dans la lightbox
            - data-enable-background : ressources img pour afficher un arrière-plan (flou blur() modifiable dans la CSS)

            Nécessite en option https://github.com/mattbryson/TouchSwipe-Jquery-Plugin pour la gestion du swipe sur mobile

            MISC variables utilisées :

            Un objet `GLOBALS.l18n` (voir source de la page) est nécessaire pour labelliser les attributs aria-
            Taper console.table(GLOBALS.l18n) sous chrome pour afficher les valeurs

            DOM = {
                window:        $(window),
                document:    $(document),
                body:        $('body')
            };

            const FOCUSABLE_SELECTORS = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"], [contenteditable]';

            const KEYCODES = {
                BACKSPACE: 8,
                TAB: 9,
                ENTER: 13,
                ESC: 27,
                SPACE: 32,
                LEFT: 37,
                UP: 38,
                RIGHT: 39,
                DOWN: 40
            };


        */

        'use strict';

        $.lightbox = async () => {

            let blocs = $('.lightbox[data-full]').filter( function() {
                    // filter for images in owl.carousel loop or others...
                    return $(this).parents('.cloned').length == 0
                });

            if(typeof $.fn.doOnce !== 'function') {
                $.fn.doOnce = async function( func ) {
                    this.length && func.apply( this );
                    return this;
                };
            }

            if(typeof $.preload !== 'function') {
                $.preload = function() {
                    let imgs = (Object.prototype.toString.call( arguments[ 0 ]) === '[object Array]' ? arguments[ 0 ] : arguments),
                        tmp = [],
                        i = imgs.length;

                    for(; i--; ) {
                        tmp.push( $('<img />', {
                            'src': imgs[i]
                        }));
                    }
                };
            }

            if(typeof $.means !== 'function') {
                $.means = function(wording) {

                    let sentence = undefined;

                    if(typeof GLOBALS === "object") {
                        sentence = GLOBALS.l18n[wording] || undefined;
                    }

                    return sentence !== undefined ? sentence : wording + ' is not defined in GLOBALS.l10n';
                };
            }

            if(blocs.length) {

                let lbx_timer,
                    placeholder = 'data:image/gif;base64,R0lGODlhCgAKAJECAAAAAP///wAAAAAAACH5BAEAAAIALAAAAAAKAAoAAAIIlI+py+0PYysAOw==',
                    count = blocs.length;

                if(typeof $.zindex !== 'function') {
                    $.zindex = async function (fromDomNode) {
                        let r = [];

                        $("*", (fromDomNode == null ? DOM.body : fromDomNode )).each( function () {
                            let z = parseInt($(this).css('z-index'), 10);
                            !isNaN(z) && r.push(z)
                        });

                        let last = r.sort(function (a, b) {
                            return a-b
                        }).reverse()[0];

                        if(last === undefined) {
                            last =    0;
                        }

                        return last + 1;

                    };
                }

                if(typeof $.scrollTop !== 'function') {
                    $.scrollTop = async function(time) {

                        $('html, body').animate({
                            scrollTop: (arguments[1] !== undefined ? arguments[1] : 0)
                        }, (isNaN(time) ? 500 : time));

                    };
                }

                lbx_timer = 0;

                $('#lightbox').doOnce( function() {
                    this.remove();
                });

                let classContainer    = 'lightbox-container',
                    classTrigger    = 'lightbox-trigger',
                    lbx_            = $('<section/>', {'id':'lightbox', 'tabindex': '0', 'style': 'z-index:' + $.zindex()}),
                    lbx_image        = $('<img/>', {'alt': '', 'src': placeholder, 'crossorigin': 'anonymous', 'decoding': 'async', 'id': 'lightbox_image'}),
                    lbx_background    = $('<span/>', {'id':'lightbox_background'}),
                    lbx_caption        = $('<div/>', {'id':'lightbox_caption', 'tabindex': '0'}),
                    lbx_controls    = $('<div/>', {'id':'lightbox_control'}),
                    lbx_first        = $('<button/>', {'id':'lightbox_first', 'aria-label': $.means('lightbox_first')}),
                    lbx_prev        = $('<button/>', {'id':'lightbox_prev', 'aria-label': $.means('lightbox_prev')}),
                    lbx_next        = $('<button/>', {'id':'lightbox_next', 'aria-label': $.means('lightbox_next')}),
                    lbx_last        = $('<button/>', {'id':'lightbox_last', 'aria-label': $.means('lightbox_last')}),
                    lbx_play        = $('<button/>', {'id':'lightbox_play', 'aria-label': $.means('lightbox_play')}),
                    lbx_loading        = $('<div/>', {'id':'lightbox_loading'}),
                    lbx_close        = $('<button/>', {'id': 'lightbox_close', 'aria-label':  $.means('lightbox_close')}),
                    lbx_next_image, lbx_prev_image,

                    lbx_fadeout = async () => {
                        lbx_.fadeOut(),
                        lbx_stop();
                        $(lbx_.data('opener')).trigger('focus');
                    },

                    lbx_autostart = false,

                    lbx_stop = async () => {
                        lbx_autostart = false;
                        lbx_play.removeClass('play'),
                        resetTimer();
                    },

                    initTimer = async () => {
                        resetTimer();
                        lbx_timer = setTimeout(function() {
                            slide();
                        }, 5000);
                    },

                    resetTimer = async () => {
                        ( (lbx_timer > 0) && clearTimeout(lbx_timer) );
                        lbx_timer = 0;
                    },

                    slide = async () => {

                        $(blocs[lbx_next_image]).parents('.' + classContainer).find('.' + classTrigger).trigger('click');

                        (lbx_autostart) && initTimer();
                    };

                DOM.body.append(lbx_);

                lbx_.append(
                    lbx_caption,
                    lbx_close,
                    lbx_image,
                    lbx_loading,
                    lbx_controls
                );

                if(count > 1) {
                    lbx_controls.append(lbx_first, lbx_prev, lbx_play, lbx_next, lbx_last);

                    lbx_.addClass('multi-views');

                }

                lbx_close.on('click', () => {
                    lbx_fadeout();
                });

                blocs.each( function(index) {

                    let element_ = $(this),
                        parentContainer = element_.parent();

                    if(!parentContainer.hasClass(classContainer)) {
                        parentContainer.addClass(classContainer);

                        ($.inArray(parentContainer.css('position') , ['absolute', 'relative', 'fixed']) === -1) && parentContainer.addClass('position-relative');

                        parentContainer.append(
                            $('<button/>')
                                .addClass(classTrigger)
                                .css({'z-index': $.zindex(parentContainer)})
                                .text($.means('lightbox_trigger'))
                        );
                    }

                    element_.hasClass('lightbox-overlay-trigger') && parentContainer.addClass('lightbox-overlay');

                    parentContainer.find('.' + classTrigger).on('click', async (ev) => {

                        ev.stopPropagation();

                        lbx_.data('opener', $(document.activeElement));

                        parentContainer.tagName().toLowerCase() === 'a' && ev.preventDefault();

                        // preload images
                        if(element_.data('enable-background') !== undefined) {
                            lbx_.prepend(lbx_background);
                            $.preload(element_.data('enable-background'));

                        } else {
                            if(typeof element_.data('full') === 'string') {
                                $.preload([element_.data('enable-background'), element_.data('full')]);
                            }
                        }

                        // ajouter un lien de téléchargement de l'image dans la lightbox
                        // attribut data-download="//lien.download."

                        if(element_.data('download') !== undefined) {

                            lbx_.removeClass('.downloadable').find('.lightbox_download').first().remove();

                            if(element_.data('download') !== false) {
                                let lbx_download = $('<img/>', {
                                    'alt': '',
                                    'src': placeholder,
                                    'class': 'lightbox_download'
                                });

                                lbx_.addClass('downloadable');
                                lbx_caption.before(lbx_download);

                                lbx_download.on('click', () => {
                                    lbx_stop();
                                    document.location = element_.data('download');
                                });
                            }
                        }

                        // ajouter un lien vers une page détails de l'image
                        // attribut data-link="//lien.vers.longdescription.image"

                        if(element_.data('link') !== undefined) {

                            lbx_.removeClass('.linkable').find('.lightbox_link').first().remove();

                            if(element_.data('link') !== false) {
                                let lbx_link = $('<a/>', {'href': element_.data('link'), 'class': 'lightbox_link'});

                                lbx_.addClass('linkable');
                                lbx_caption.before( lbx_link.text($.means('lightbox_link') ));

                                lbx_link.on('click', () => {
                                    lbx_stop();
                                });
                            }
                        }

                        (lbx_.is(':hidden')) && (
                            lbx_.fadeIn(),
                            lbx_play.removeClass('play')
                        );

                        lbx_next_image = index + 1;
                        (lbx_next_image > count - 1) && (lbx_next_image = 0);

                        lbx_prev_image = index - 1;
                        (lbx_prev_image < 0) && (lbx_prev_image = count - 1);

                        let index_images = $.means('lightbox_enum') + (index + 1)  + $.means('lightbox_on') + count,
                            img_caption = element_.attr('title') || element_.attr('data-title') || null;

                        lbx_caption.empty();

                        (img_caption != null && img_caption.trim().length) && lbx_caption.prepend($('<div/>').text(img_caption));


                        lbx_controls.attr('data-before', index_images);

                        lbx_image.animate({opacity: 0}, 50, () => {

                            lbx_.addClass('loading').css({zIndex: $.zindex()}),

                            lbx_loading.fadeIn(50);

                            if(typeof element_.data('full') !== 'string') {

                                let values_ = element_.data('full');

                                lbx_image.attr({
                                    'src': values_.src,
                                    'srcset': values_.srcset.join(',')
                                });
                            } else {

                                lbx_image.attr({
                                    'src': element_.data('full')
                                });
                            }

                            lbx_image.on('load', async () => {

                                lbx_.attr({'aria-live': 'assertive', 'aria-modal': 'true'}).focus();
                                lbx_image.css({maxHeight: 'calc(100% - '+ ( lbx_close.outerHeight() + lbx_controls.outerHeight(true) ) + 'px)'});

                                lbx_loading.hide();

                                lbx_image.animate({opacity: 1}, 50, async () => {

                                    lbx_.removeClass('loading');

                                    // ajouter l'image d'arrière plan

                                    if(element_.data('enable-background') !== undefined) {

                                        lbx_background.css({
                                            backgroundImage: 'url(' + element_.data('enable-background') + ')'
                                        }).addClass('loaded').on('click', async () => {
                                            lbx_fadeout();
                                        });
                                    }

                                }).dequeue();

                                let scrollToItem = $('[data-scroll="' + element_.data('scroll') + '"]');

                                scrollToItem.length && $.scrollTop(0, scrollToItem.offset().top.toFixed(0) - element_.outerHeight());

                                let focusable = lbx_.find(FOCUSABLE_SELECTORS);

                                if (focusable.length) {

                                    let start_ = focusable[0],
                                        end_ = focusable[focusable.length - 1],
                                        focus_ = lbx_ ? lbx_ : start_;

                                    focus_.focus();

                                    lbx_.on('keydown', async (event) => {
                                        if (event.keyCode === KEYCODES.TAB) {

                                            (event.shiftKey && document.activeElement === start_) && (
                                                event.preventDefault(),
                                                    end_.focus()
                                            );

                                            (!event.shiftKey && document.activeElement === end_) && (
                                                event.preventDefault(),
                                                    start_.focus()
                                            );

                                        }

                                    });

                                }


                                if(lbx_autostart) {
                                    initTimer()
                                }
                            });

                            lbx_.on('click', async (e) => {
                                $(e.target).attr('id') === lbx_.attr('id') && lbx_fadeout();
                            });

                        }).dequeue();

                    });

                });

                lbx_play.on('click', async () => {

                    lbx_play.toggleClass('play');

                    if(lbx_autostart) {
                        lbx_autostart = false;
                        resetTimer();
                    } else {
                        lbx_autostart = true;
                        initTimer();
                    }

                });

                lbx_image.on('click', async () => {
                    count > 1 ? lbx_next.trigger('click') : (document.fullscreenElement == null ? $.fullScreenOn(document.getElementById('lightbox_image')): ($.fullScreenOff(document.getElementById('lightbox_image')), lbx_.trigger('click')));
                }),

                lbx_first.on('click', async () => {
                    lbx_.is(':visible') && $(blocs[0]).parents('.' + classContainer).find('.' + classTrigger).trigger('click');
                    lbx_stop();
                }),

                lbx_prev.on('click', async () => {
                    lbx_.is(':visible') && $(blocs[lbx_prev_image]).parents('.' + classContainer).find('.' + classTrigger).trigger('click');
                    lbx_stop();
                }),

                lbx_next.on('click', async () => {
                    lbx_.is(':visible') && $(blocs[lbx_next_image]).parents('.' + classContainer).find('.' + classTrigger).trigger('click');
                    lbx_stop();
                }),

                lbx_last.on('click', async () => {
                    lbx_.is(':visible') && $(blocs[count - 1]).parents('.' + classContainer).find('.' + classTrigger).trigger('click');
                    lbx_stop();
                });



                if('ontouchstart' in window === false) {

                    DOM.window.on('keyup', async ev => {

                        if(lbx_.is(':visible') && $.inArray(ev.which, [KEYCODES.ESC, KEYCODES.LEFT, KEYCODES.RIGHT]) !== -1) {
                            if(count > 1) {
                                if(ev.which === KEYCODES.RIGHT) {
                                    $(blocs[lbx_next_image]).parents('.' + classContainer).find('.' + classTrigger).trigger('click');
                                }

                                if(ev.which === KEYCODES.LEFT) {
                                    $(blocs[lbx_prev_image]).parents('.' + classContainer).find('.' + classTrigger).trigger('click');
                                }

                            }

                            ev.which === KEYCODES.ESC && lbx_fadeout();

                        }
                    });

                }

                if(typeof $.fn.swipe === 'function') {
                    if(('ontouchstart' in window === true) && count > 1) {
                        lbx_image.swipe( {
                            swipeLeft:function(event, direction, distance, duration, fingerCount) {
                                if(lbx_.is(':visible')) {
                                    $(blocs[lbx_next_image]).parents('.' + classContainer).find('.' + classTrigger).trigger('click');
                                }

                            },
                            swipeRight:function(event, direction, distance, duration, fingerCount) {
                                if(lbx_.is(':visible')) {
                                    $(blocs[lbx_prev_image]).parents('.' + classContainer).find('.' + classTrigger).trigger('click');
                                }
                            },
                            threshold: 100
                        });
                    }
                } else {
                    console.log('need https://github.com/mattbryson/TouchSwipe-Jquery-Plugin');
                }

            }

        };

        DOM.document.ready( async () => {
            $.lightbox();
        });


    })(jQuery);

    Source CSS

    .lightbox-trigger {
        background-image:url('/svg/lightbox.svg#i-zoomin-dark');
        background-position: center center !important;
        background-repeat: no-repeat  !important;
        background-color: rgba(255, 0) !important;
        background-size: 70% 70% !important;
        border-radius: var(--radius) 0;
        color: rgba(0, 0) !important;
        white-space: nowrap;
        text-overflow: clip;
        overflow: hidden;
        min-width: 2.5rem !important;
        min-height: 2.5rem !important;
        max-width: 3.75rem;
        max-height: 3.75rem;
        height: 4vw;
        width: 4vw;
        position: absolute !important;
        left: 0;
        top: 0;
    }

    .lightbox-overlay .lightbox-trigger {
        max-width: unset;
        max-height: unset;
        border-radius: var(--radius);
    }

    @media (any-hover: hover) {
        .lightbox-overlay .lightbox-trigger {
            top: 0;
            left: 0;
            bottom: 0;
            right: 0;
            width: 100%;
            height: auto !important;
            max-width: 100%;
            max-height: unset;
            cursor: zoom-in !important;
            opacity: 0;
            background-size: 2.5rem 2.5rem !important;
        }

        .lightbox-trigger {
            cursor: pointer !important;
        }

        .lightbox-container.lightbox-overlay:hover .lightbox-trigger {
            background-color: rgba(0, 50) !important;
            opacity: 1;
        }
    }

    .lightbox-container .lightbox-trigger:focus {
        background-color: rgba(0, 20);
        opacity: 1;
    }

    #lightbox {

        --lightbox-baseline: 3.125rem;
        --lightbox-size-icons: 0.875rem;

        bottom: 0;
        color: white;
        display: none;
        left: 0;
        position: fixed;
        right: 0;
        top: 0;
        background-color: #000000;
        outline: none;
    }

    #lightbox.loading {
        background-image: url('/svg/lightbox.svg#i-loader');
        background-position: center center;
        background-size: var(--lightbox-size-icons) var(--lightbox-size-icons);
    }

    #lightbox_background {
        position: absolute;
        left: -5vw;
        top: -5vh;
        width: 110vw;
        height: 110vh;
        opacity: .3;
        background-size: cover;
        background-position: center center;
    }

    #lightbox.loading #lightbox_background {
        opacity: 0;
    }

    #lightbox_background.loaded {
        opacity: .3;
    }

    #lightbox_caption {
        top: 0;
        padding-left: 1.25rem;
        left: 0;
        position: absolute;
        right: var(--lightbox-baseline);
    }

    #lightbox.downloadable #lightbox_caption, #lightbox.linkable #lightbox_caption {
        left: var(--lightbox-baseline);
    }

    #lightbox.downloadable.linkable #lightbox_caption {
        left: calc(var(--lightbox-baseline) * 2);
    }

    #lightbox_caption div {
        line-height: var(--lightbox-baseline);
        padding-right: 1.875rem;
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }

    #lightbox_image {
        height: auto;
        width: auto;
        max-width: calc(100vw - 0.625rem);
        position: absolute;
        left: 50%;
        top: 50%;
        box-shadow: 0 0 0 1px rgba(200, 20) inset;
        padding: 1px;
        -webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);
    }

    #lightbox.multi-views #lightbox_image {
        margin-top: -0.625rem;
    }

    #lightbox_loading {
        left: 50%;
        top: calc(50% - 2rem);
        line-height: 2rem;
        position: absolute;
        display: none;
        border-radius: var(--radius);
        -webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);
    }

    #lightbox_control {
        position: absolute;
        display: inline-block;
        left: 0;
        right: 0;
        bottom: 0;
        padding-top: calc(var(--lightbox-baseline) / 2);
        min-height: var(--lightbox-baseline);
        white-space: nowrap;
        text-align: center;
    }

    #lightbox_control:before {
        content: attr(data-before);
        position: absolute;
        top: 0;
        left: 50%;
        line-height: calc(var(--lightbox-baseline) / 2);
        display: inline-block;
        white-space: nowrap;
        opacity: .8;
        font-variant: small-caps;
        font-size: 0.8125rem;
        -webkit-transform:translateX(-50%);-moz-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%);
    }

    #lightbox_close {
        background-image: url('/svg/lightbox.svg#i-close');
        background-position: center center;
        background-color: transparent !important;
        background-size: var(--lightbox-size-icons) var(--lightbox-size-icons);
        cursor: pointer;
        height: var(--lightbox-baseline);
        width: var(--lightbox-baseline);
        position: absolute;
        right: 0;
        top: 0;
    }

    .lightbox_download, .lightbox_link, #lightbox_control button {
        background-position: center center;
        height: var(--lightbox-baseline);
        width: var(--lightbox-baseline);
        cursor: pointer;
    }

    .lightbox_download {
        position: absolute;
        top: 0;
        left: 0;
        background-size: var(--lightbox-size-icons) var(--lightbox-size-icons);
        background-color: transparent !important;
    }

    .lightbox_link {
        position: absolute;
        top: 0;
        left: 0;
        background-size: var(--lightbox-size-icons) var(--lightbox-size-icons);
        color: transparent !important;
        white-space: nowrap;
        overflow: hidden;
        background-color: transparent !important;
    }

    .lightbox_download + .lightbox_link {
        left: var(--lightbox-baseline);
    }

    #lightbox_control button  {
        background-size: var(--lightbox-size-icons) var(--lightbox-size-icons);
        vertical-align: bottom;
        background-color: transparent !important;
    }

    #lightbox_first {
        background-image: url('/svg/lightbox.svg#i-first');
    }

    #lightbox_prev {
        background-image: url('/svg/lightbox.svg#i-prev');
    }

    #lightbox_play.play {
        background-image: url('/svg/lightbox.svg#i-pause');
    }

    #lightbox_play:not(.play) {
        background-image: url('/svg/lightbox.svg#i-play');
    }

    #lightbox_next {
        background-image: url('/svg/lightbox.svg#i-next');
    }

    #lightbox_last {
        background-image: url('/svg/lightbox.svg#i-last');
    }

    .lightbox_download {
        background-image: url('/svg/lightbox.svg#i-download');
    }

    .lightbox_link {
        background-image: url('/svg/lightbox.svg#i-linkto');
    }

    Source SVG

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
        <title>Lightbox icons — Kortic</title>
        <defs>
            <style>
                .fill-color {
                    fill: #e7e7e7;
                }
                .fill-green {
                    fill: #1ba39c;
                }
            </style>
        </defs>
        <symbol id="first" viewBox="0 0 32 32">
            <path d="M5.66,0A1.66,1.66,0,0,0,4,1.66H4V30.34A1.66,1.66,0,0,0,5.66,32H8.71a1.66,1.66,0,0,0,1.65-1.66V1.66A1.66,1.66,0,0,0,8.71,0H5.66Z" class="fill-color"/>
            <path d="M28.15,1.37,27.67.94a2.65,2.65,0,0,0-3.78.13l-.1.11L11,14a2.7,2.7,0,0,0-.21,3.79A2.08,2.08,0,0,0,11,18L23.79,30.82a2.67,2.67,0,0,0,3.77.34l.11-.1.48-.48Z" class="fill-color"/>
        </symbol>

        <symbol id="prev" viewBox="0 0 32 32">
            <path d="M15.15,18a2.86,2.86,0,0,1,0-4.05h0c3.21-3.21,6.39-6.39,9.6-9.54a2.17,2.17,0,0,0,.11-3.07.93.93,0,0,0-.14-.14L24.25.77a2.65,2.65,0,0,0-3.75,0,2.2,2.2,0,0,0-.2.23l-13,13a2.86,2.86,0,0,0,0,4h0l13,13a2.64,2.64,0,0,0,3.72.44l.23-.2.47-.48a2.12,2.12,0,0,0,.32-3,1.8,1.8,0,0,0-.19-.2C21.51,24.42,18.36,21.18,15.15,18Z" class="fill-color"/>
        </symbol>

        <symbol id="play" viewBox="0 0 32 32">
            <path d="M9.11.35A1.6,1.6,0,0,0,6.84.64a1.69,1.69,0,0,0-.34,1V30.34a1.67,1.67,0,0,0,.91,1.5,1.6,1.6,0,0,0,.71.16,1.55,1.55,0,0,0,1-.35L26.84,17.31A1.69,1.69,0,0,0,27.19,15a1.93,1.93,0,0,0-.35-.35Z" class="fill-color"/>
        </symbol>

        <symbol id="pause" viewBox="0 0 32 32">
            <circle cx="16" cy="16" r="16" class="fill-color"/>
            <path d="M12,7.8a.83.83,0,0,0-.83.83V22.89a.83.83,0,0,0,.83.83h1.53a.83.83,0,0,0,.83-.83h0V8.63a.83.83,0,0,0-.83-.83Z"/>
            <path d="M18.51,7.8a.83.83,0,0,0-.83.83h0V22.89a.83.83,0,0,0,.83.83H20a.83.83,0,0,0,.83-.83h0V8.63A.83.83,0,0,0,20,7.8H18.51Z"/>
        </symbol>

        <symbol id="next" viewBox="0 0 32 32">
            <path d="M16.74,14a2.87,2.87,0,0,1,0,4h0c-3.21,3.21-6.39,6.39-9.59,9.54a2.18,2.18,0,0,0,0,3.08l.15.13.47.45a2.65,2.65,0,0,0,3.75,0,2.2,2.2,0,0,0,.2-.23l13-13a2.86,2.86,0,0,0,0-4.05h0L11.7,1A2.64,2.64,0,0,0,8,.57l-.23.2-.47.48a2.12,2.12,0,0,0-.32,3,1.8,1.8,0,0,0,.19.2C10.38,7.58,13.53,10.82,16.74,14Z" class="fill-color"/>
        </symbol>

        <symbol id="last" viewBox="0 0 32 32">
            <path d="M23.44,0a1.67,1.67,0,0,0-1.65,1.66V30.34A1.67,1.67,0,0,0,23.44,32h3a1.66,1.66,0,0,0,1.66-1.66V1.66h0A1.66,1.66,0,0,0,26.49,0Z" class="fill-color"/>
            <path d="M4,30.58l.48.48.11.1h0a2.68,2.68,0,0,0,3.76-.35L21.15,18a2.08,2.08,0,0,0,.21-.21A2.7,2.7,0,0,0,21.15,14L8.36,1.18l-.1-.11,0,0A2.65,2.65,0,0,0,4.48.94L4,1.37Z" class="fill-color"/>
        </symbol>

        <symbol id="close" viewBox="0 0 32 32">
            <path d="M30,0a1.95,1.95,0,0,1,2,2,1.91,1.91,0,0,1-.58,1.42L18.83,16,31.42,28.58A1.91,1.91,0,0,1,32,30a1.95,1.95,0,0,1-2,2,1.91,1.91,0,0,1-1.42-.58L16,18.83,3.42,31.42A1.91,1.91,0,0,1,2,32a1.95,1.95,0,0,1-2-2,1.91,1.91,0,0,1,.58-1.42L13.17,16,.58,3.42A1.91,1.91,0,0,1,0,2,1.95,1.95,0,0,1,2,0,1.91,1.91,0,0,1,3.42.58L16,13.17,28.58.58A1.91,1.91,0,0,1,30,0Z" class="fill-color"/>
        </symbol>

        <symbol id="download" viewBox="0 0 32 32">
            <path d="M15,25.39h0a1.6,1.6,0,0,0,.38.23h0a1.83,1.83,0,0,0,.56.13h.2a1.58,1.58,0,0,0,.57-.13h0a1.46,1.46,0,0,0,.33-.22h0l.08-.08,6.39-6.39a1.58,1.58,0,0,0-.6-2.17,1.6,1.6,0,0,0-1.53,0L17.77,20.4V1.85a1.6,1.6,0,1,0-3.2,0V20.4l-3.66-3.67a1.61,1.61,0,0,0-2.26.13,1.63,1.63,0,0,0,0,2.13L15,25.39Z" class="fill-color"/>
            <path d="M30.4,20a1.6,1.6,0,0,0-1.59,1.6v4.8a1.62,1.62,0,0,1-1.6,1.6H4.79a1.62,1.62,0,0,1-1.6-1.6V21.6A1.6,1.6,0,1,0,0,21.6H0v4.8a4.81,4.81,0,0,0,4.81,4.81h22.4A4.81,4.81,0,0,0,32,26.4V21.6A1.62,1.62,0,0,0,30.4,20Z" class="fill-color"/>
        </symbol>

        <symbol id="linkto" viewBox="0 0 32 32">
            <path d="M13.5,18.45a1.73,1.73,0,0,1,.08,2.38l-.08.08a1.72,1.72,0,0,1-2.48,0l0,0A8.81,8.81,0,0,1,11,8.76l.12-.12h0L17.18,2.5a8.77,8.77,0,0,1,12.26,0,8.65,8.65,0,0,1,.35,11.92l-.35.35-2.61,2.62a12.26,12.26,0,0,0-.72-4.16l.83-.84a5.24,5.24,0,0,0,.13-7.23L26.94,5a5.26,5.26,0,0,0-7.37,0L13.5,11.09a5.25,5.25,0,0,0-.12,7.24l.12.12m5-7.36a1.73,1.73,0,0,1,2.5,0,8.82,8.82,0,0,1,.12,12.15l-.12.12h0L14.82,29.5a8.77,8.77,0,0,1-12.26,0,8.65,8.65,0,0,1-.35-11.92c.12-.12.23-.24.35-.35l2.61-2.62a12.26,12.26,0,0,0,.72,4.16l-.83.84a5.24,5.24,0,0,0-.13,7.23,1.59,1.59,0,0,0,.13.13,5.26,5.26,0,0,0,7.37,0l6.07-6.06a5.25,5.25,0,0,0,.12-7.24l-.12-.12a1.6,1.6,0,0,1-.26-2.19A1.66,1.66,0,0,1,18.5,11.09Z" class="fill-color"/>
        </symbol>

        <symbol id="zoomin" viewBox='0 0 32 32'>
            <path d='M26.7,25.4l-3.2-3.2a15.3,15.3,0,0,1-1.8-1.8c-.1-.1-.3-.2-.3-.3a9.22,9.22,0,0,1,.5-.8,7.46,7.46,0,0,0,.7-1.2A9.28,9.28,0,0,0,15.1,5,9.41,9.41,0,0,0,5.2,16.2a9.35,9.35,0,0,0,13.9,6c.4-.2.8-.8,1.1-.6a4.62,4.62,0,0,1,.7.7l2.4,2.4c.6.6,1.2,1.3,1.9,1.9a1,1,0,0,0,.7.4A1,1,0,0,0,26.7,25.4ZM14.3,21.7a7.57,7.57,0,0,1-7-4.9A7.48,7.48,0,0,1,9.6,8.5c5.2-4.3,13.2.5,12,7.1a7.57,7.57,0,0,1-7.3,6.1Z'/>
            <path d='M18.94,13.44H15.13V9.63a.85.85,0,1,0-1.69,0v3.81H9.63a.84.84,0,0,0-.84.84.85.85,0,0,0,.84.85h3.81v3.81a.85.85,0,0,0,1.69,0V15.13h3.81a.85.85,0,0,0,.85-.85A.84.84,0,0,0,18.94,13.44Z'/>
        </symbol>


        <symbol id="loader" viewBox='0 0 128 128'>
            <g class="fill-color">
            <path d='M63.6,0L63.6,0c2.2,0,4,1.8,4,4v32c0,2.2-1.8,4-4,4h0c-2.2,0-4-1.8-4-4V4C59.6,1.8,61.4,0,63.6,0z'/>
            <path fill-opacity='0' d='M95.7,8.4L95.7,8.4c1.9,1.1,2.6,3.6,1.5,5.5l-16,27.7c-1.1,1.9-3.6,2.6-5.5,1.5l0,0c-1.9-1.1-2.6-3.6-1.5-5.5 l16-27.7C91.3,7.9,93.7,7.3,95.7,8.4z'/>
            <path fill-opacity='.1' d='M119.2,31.7L119.2,31.7c1.1,1.9,0.4,4.4-1.5,5.5L90,53.1c-1.9,1.1-4.4,0.4-5.5-1.5l0,0 c-1.1-1.9-0.4-4.4,1.5-5.5l27.7-16C115.7,29.1,118.1,29.7,119.2,31.7z'/>
            <path fill-opacity='.1' d='M128,63.6L128,63.6c0,2.2-1.8,4-4,4H92c-2.2,0-4-1.8-4-4v0c0-2.2,1.8-4,4-4h32C126.2,59.6,128,61.4,128,63.6z' />
            <path fill-opacity='.2' d='M119.6,95.7L119.6,95.7c-1.1,1.9-3.6,2.6-5.5,1.5l-27.7-16c-1.9-1.1-2.6-3.6-1.5-5.5l0,0 c1.1-1.9,3.6-2.6,5.5-1.5l27.7,16C120.1,91.3,120.7,93.7,119.6,95.7z'/>
            <path fill-opacity='.3' d='M96.3,119.2L96.3,119.2c-1.9,1.1-4.4,0.4-5.5-1.5L74.9,90c-1.1-1.9-0.4-4.4,1.5-5.5l0,0 c1.9-1.1,4.4-0.4,5.5,1.5l16,27.7C98.9,115.7,98.3,118.1,96.3,119.2z'/>
            <path fill-opacity='.4' d='M64.4,128L64.4,128c-2.2,0-4-1.8-4-4V92c0-2.2,1.8-4,4-4h0c2.2,0,4,1.8,4,4v32C68.4,126.2,66.6,128,64.4,128z' />
            <path fill-opacity='.5' d='M32.3,119.6L32.3,119.6c-1.9-1.1-2.6-3.6-1.5-5.5l16-27.7c1.1-1.9,3.6-2.6,5.5-1.5l0,0 c1.9,1.1,2.6,3.6,1.5,5.5l-16,27.7C36.7,120.1,34.3,120.7,32.3,119.6z'/>
            <path fill-opacity='.6' d='M8.8,96.3L8.8,96.3c-1.1-1.9-0.4-4.4,1.5-5.5l27.7-16c1.9-1.1,4.4-0.4,5.5,1.5l0,0c1.1,1.9,0.4,4.4-1.5,5.5 l-27.7,16C12.3,98.9,9.9,98.3,8.8,96.3z'/>
            <path fill-opacity='.7' d='M0,64.4L0,64.4c0-2.2,1.8-4,4-4h32c2.2,0,4,1.8,4,4v0c0,2.2-1.8,4-4,4H4C1.8,68.4,0,66.6,0,64.4z'/>
            <path fill-opacity='.8' d='M8.4,32.3L8.4,32.3c1.1-1.9,3.6-2.6,5.5-1.5l27.7,16c1.9,1.1,2.6,3.6,1.5,5.5l0,0c-1.1,1.9-3.6,2.6-5.5,1.5 l-27.7-16C7.9,36.7,7.3,34.3,8.4,32.3z'/>
            <path fill-opacity='.9' d='M31.7,8.8L31.7,8.8c1.9-1.1,4.4-0.4,5.5,1.5l16,27.7c1.1,1.9,0.4,4.4-1.5,5.5l0,0c-1.9,1.1-4.4,0.4-5.5-1.5 l-16-27.7C29.1,12.3,29.7,9.9,31.7,8.8z'/>
            <animateTransform accumulate='none' additive='replace' attributeName='transform' calcMode='discrete' dur='750ms' fill='remove' repeatCount='indefinite' restart='always' type='rotate' values='0 64 64;30 64 64;60 64 64;90 64 64;120 64 64;150 64 64;180 64 64;210 64 64;240 64 64;270 64 64;300 64 64;330 64 64'></animateTransform>
            </g>
        </symbol>


        <view id="i-first"                 viewBox="10 10 32 32"/>
        <use xlink:href="#first"             x="10" y="10" width="32" height="32"/>

        <view id="i-prev"                 viewBox="60 10 32 32"/>
        <use xlink:href="#prev"             x="60" y="10" width="32" height="32"/>

        <view id="i-play"                 viewBox="110 10 32 32"/>
        <use xlink:href="#play"             x="110" y="10" width="32" height="32"/>

        <view id="i-pause"                 viewBox="160 10 32 32"/>
        <use xlink:href="#pause"             x="160" y="10" width="32" height="32"/>

        <view id="i-next"                 viewBox="210 10 32 32"/>
        <use xlink:href="#next"             x="210" y="10" width="32" height="32"/>

        <view id="i-last"                 viewBox="260 10 32 32"/>
        <use xlink:href="#last"             x="260" y="10" width="32" height="32"/>

        <view id="i-close"                 viewBox="310 10 32 32"/>
        <use xlink:href="#close"             x="310" y="10" width="32" height="32"/>

        <view id="i-download"             viewBox="360 10 32 32"/>
        <use xlink:href="#download"         x="360" y="10" width="32" height="32"/>

        <view id="i-linkto"                 viewBox="410 10 32 32"/>
        <use xlink:href="#linkto"             x="410" y="10" width="32" height="32"/>

        <view id="i-zoomin"                 viewBox="460 10 32 32"/>
        <use xlink:href="#zoomin"             x="460" y="10" width="32" height="32" class="fill-green"/>

        <view id="i-zoomin-dark"             viewBox="510 10 34 34"/>
        <!--<use xlink:href="#zoomin"             x="511" y="11" width="32" height="32" fill-opacity=".3"/>-->
        <use xlink:href="#zoomin"             x="510" y="10" width="32" height="32" class="fill-green" />

        <view id="i-loader"                 viewBox="10 100 32 32"/>
        <use xlink:href="#loader"             x="10" y="100" width="32" height="32"/>


    </svg>
    Si vous souhaitez participer, même modestement, au maintien du site et pour son usage, vous pouvez cliquer sur le joli bouton…
    Soutenez Aquinum
    Voir aussi…
    • Vérifier un numéro de carte bancaire Vérification de carte bancaire, script de validation de carte bancaire. Contrôle de format de numéro de carte bancaire, algorithme de Luhn carte de crédit Visa, Mastercard, Maestro, CB, Carte Bancaire, Discover Card, American Express, JCB Cards
    • Owl Carousel multiple Gérer plusieurs carrousels/sliders sur une seule page avec Owl Carousel 2. Manage multiple carousels/sliders on one page with Owl Carousel 2
    • Conversion SVG/CSS Encodage SVG pour CSS compatible Sass et exemple d'intégration SVG via xlink compatible CSS. SVG encoder, base64 SVG pour CSS.
    • Optimisation SVG Intégration SVG via xlink/use/symbol, factorisation de symbol SVG
    • Responsive SVG Utilisation des media queries internes au SVG
    • LazyLoad Intersection Observer LazyLoad avec IntersectionObserver API pour images, picture, iframe, etc.
    • Design, flux et accessibilité jQuery Responsive layout logical DOM plugin
    • Lightbox Responsive et Accessible Lightbox images jQuery Responsive accessible a11y. Automatisation d'une Lightbox multipages. Lightbox image unique ou multiple. Lightbox responsive et accessible a11y wcag
    • Tooltip (infobulle) a11y Tooltip (infobulle) compatible a11y et SEO, naviguable au clavier, accessible par lecteur d'écran
    • Zipcode patterns Contrôle automatisé de codes postaux internationaux
    • Responsive background-image jQuery responsive background-image plugin
    • Drapeaux ISO 3366 SVG Drapeaux nationaux vectoriels normés ISO alpha 2, alpha 3 et numérotation Organisation des Nations Unies (ONU). SVG drapeaux pour internationalisation de sites multilingues.
    • Layout rigolo Une autre approche pour définir un layout de page responsive.
    • Générateur de noms API de génération de couples prénom/nom aléatoires.
    2020-07-12