]> _ Git - Animations.git/commitdiff
wip #5026 @0.5
authorVincent Vanwaelscappel <vincent@cubedesigners.com>
Thu, 24 Nov 2022 11:06:37 +0000 (12:06 +0100)
committerVincent Vanwaelscappel <vincent@cubedesigners.com>
Thu, 24 Nov 2022 11:06:37 +0000 (12:06 +0100)
Discac/checkbox.png [deleted file]
Discac/index.html [deleted file]
Discac/jquery.multi-select.js [deleted file]
Michelin/Michelin.zip [new file with mode: 0644]
Michelin/_doc/publish.bat [new file with mode: 0644]
Michelin/checkbox.png [new file with mode: 0644]
Michelin/index.html [new file with mode: 0644]
Michelin/jquery.multi-select.js [new file with mode: 0644]

diff --git a/Discac/checkbox.png b/Discac/checkbox.png
deleted file mode 100644 (file)
index 9163bea..0000000
Binary files a/Discac/checkbox.png and /dev/null differ
diff --git a/Discac/index.html b/Discac/index.html
deleted file mode 100644 (file)
index f8e256b..0000000
+++ /dev/null
@@ -1,512 +0,0 @@
-<!DOCTYPE html>
-<html lang="fr">
-<head>
-    <meta charset="UTF-8">
-    <meta name="width" content="440">
-    <meta name="height" content="760">
-    <title></title>
-    <link rel="preconnect" href="https://fonts.gstatic.com">
-    <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400&display=swap" rel="stylesheet">
-    <style>
-        * {
-            padding: 0;
-            margin: 0;
-        }
-
-        html, body {
-            height: 100%;
-        }
-
-        body {
-            font-family: "Open Sans", sans-serif;
-            font-size: 17px;
-            color: #fff;
-            background-color: #93c23a;
-        }
-
-        a {
-            color: #fff;
-            text-decoration: none;
-            display: block;
-            background-color: #93c23a;
-            padding: 5px 25px;
-            position: relative;
-        }
-
-        a:hover {
-            background-color: #66a11a;
-            transition: background-color 200ms;
-        }
-
-        a span {
-            position: absolute;
-            right: 25px;
-            top: 5px;
-        }
-
-        h2 {
-            padding: 25px 25px 0px;
-            font-weight: 400;
-            font-size: 17px;
-        }
-
-        main {
-            max-width: 440px;
-            margin: 0 auto;
-        }
-
-        #selects {
-            padding: 25px;
-        }
-
-        .multi-select-container {
-            display: block;
-            position: relative;
-        }
-
-        .multi-select-menu {
-            position: absolute;
-            left: 0;
-            top: 0.8em;
-            float: left;
-            min-width: 100%;
-            background: #fff;
-            margin: 1em 0;
-            padding: 0.4em 0;
-            display: none;
-            z-index: 2;
-        }
-
-
-        .multi-select-menu label {
-            color: #464646;
-            display: block;
-            padding: 3px 10px;
-            font-size: 16px;
-        }
-
-        .multi-select-menu input {
-            margin-right: 0.3em;
-            vertical-align: 0.1em;
-        }
-
-        .multi-select-button {
-            margin: 4px 0;
-            display: block;
-            color: #474747;
-            font-size: 0.875em;
-            padding: 0.2em 2em 0.2em 0.6em;
-            white-space: nowrap;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            background-color: #fff;
-            border-radius: 4px;
-            cursor: default;
-            position: relative;
-        }
-
-        .multi-select-button.active {
-            background-color: #66a11a;
-            color: #fff;
-        }
-
-        .multi-select-button.active:after {
-            border-color: #fff transparent transparent transparent;
-        }
-
-        .multi-select-container--open .multi-select-button.active::after {
-            border-color: transparent transparent #fff transparent
-        }
-
-        .multi-select-button:after {
-            content: "";
-            display: inline-block;
-            width: 0;
-            height: 0;
-            border-style: solid;
-            border-width: 0.4em 0.4em 0 0.4em;
-            border-color: #474747 transparent transparent transparent;
-            margin-left: 0.4em;
-            vertical-align: 0.1em;
-            position: absolute;
-            right: 10px;
-            top: 11px;
-        }
-
-        .multi-select-presets {
-            position: relative;
-            padding-top: 5px;
-            border-top: 1px solid #eee;
-        }
-
-        .multi-select-presets button {
-            cursor: pointer;
-            position: absolute;
-            top: 5px;
-            right: 5px;
-            background-color: #66a11a;
-            color: #fff;
-            text-transform: uppercase;
-            border: 0;
-            border-radius: 4px;
-            padding: 2px 15px;
-            font-size: 17px;
-            font-family: "Open Sans", sans-serif;
-        }
-
-        .multi-select-container--open .multi-select-menu {
-            display: block;
-        }
-
-        .multi-select-container--open .multi-select-button:after {
-            border-width: 0 0.4em 0.4em 0.4em;
-            border-color: transparent transparent #999 transparent;
-        }
-
-        .checkbox {
-            display: inline-block;
-            width: 19px;
-            height: 19px;
-            background-image: url("checkbox.png");
-            background-size: 19px auto;
-            background-repeat: no-repeat;
-            background-position: 0 100%;
-            margin-right: 5px;
-            vertical-align: top;
-            position: relative;
-            top: 1px;
-        }
-
-        .checkbox[checked] {
-            background-position: 0 0;
-        }
-
-        #emptyres {
-            padding: 0 25px;
-        }
-
-    </style>
-</head>
-<body>
-<main>
-    <h2>Filtrer ma recherche</h2>
-    <div id="selects">
-
-    </div>
-    <nav>
-        <a href="#" data-largeur="70,90,105,140" data-vasque="VS,VD,VG,VP" data-meuble="1T,2T,P" data-profondeur="z54"
-           data-poignees="yes" data-page="6">Loft<span>6</span></a>
-        <a href="#" data-largeur="70,90,140" data-vasque="VS,VD,VP" data-meuble="2T" data-profondeur="z54"
-           data-poignees="no" data-page="14">Allure<span>14</span></a>
-        <a href="#" data-largeur="70,90,140" data-vasque="VS,VD,VP" data-meuble="3T" data-profondeur="z54"
-           data-poignees="yes" data-page="20">Trendy<span>20</span></a>
-        <a href="#" data-largeur="60,80,90,120" data-vasque="VS,VD,VG,VP" data-meuble="1T,2T" data-profondeur="z46"
-           data-poignees="no" data-page="26">Rivage<span>26</span></a>
-        <a href="#" data-largeur="90,120" data-vasque="VS,VD,VG" data-meuble="2T" data-profondeur="z46"
-           data-poignees="no" data-page="36">Esquisse<span>36</span></a>
-        <a href="#" data-largeur="60,80,90,120" data-vasque="VS,VD,VG,VP" data-meuble="1T,2T,P" data-profondeur="z46"
-           data-poignees="yes" data-page="42">Swing<span>42</span></a>
-        <a href="#" data-largeur="90,120" data-vasque="VS,VD,VP" data-meuble="1T,2T" data-profondeur="z46"
-           data-poignees="yes" data-page="54">Equilibre<span>54</span></a>
-        <a href="#" data-largeur="90,120" data-vasque="VS,VD,VP" data-meuble="1T,2T" data-profondeur="z46"
-           data-poignees="yes" data-page="60">Fabrik<span>60</span></a>
-        <a href="#" data-largeur="60,80,90,120,surmesure" data-vasque="VS,VD,VG,VP" data-meuble="1T,2T,P"
-           data-profondeur="z46" data-poignees="yes" data-page="66">Osmose<span>66</span></a>
-        <a href="#" data-largeur="60,70,80,90,120" data-vasque="VS,VD" data-meuble="2T,P" data-profondeur="z46"
-           data-poignees="yes" data-page="74">Chango<span>74</span></a>
-        <a href="#" data-largeur="60,8" data-vasque="VS" data-meuble="P" data-profondeur="z36"
-           data-poignees="yes" data-page="80">Lindy<span>80</span></a>
-        <a href="#" data-largeur="40" data-vasque="VS" data-meuble="P" data-profondeur="z22" data-poignees="yes"
-           data-page="84">Lave-mains<span>84</span></a>
-        <a href="#" data-largeur="80" data-vasque="VS" data-meuble="P" data-profondeur="z54"
-           data-poignees="yes" data-page="86">Estia<span>86</span></a>
-        <a href="#" data-largeur="124" data-vasque="VG" data-meuble="2T,P" data-profondeur="z65" data-poignees="yes"
-           data-page="88">Lave-linge<span>88</span></a>
-
-
-    </nav>
-    <div id="emptyres">Aucun produit ne correspond aux filtres sélectionnés</div>
-</main>
-<script src="../../jquery.js"></script>
-<script src="jquery.multi-select.js"></script>
-<script>
-    var selects = {
-        profondeur: {
-            label: 'Profondeurs',
-            data: {
-                'z22': '22 cm',
-                'z36': '36 cm',
-                'z46': '46 cm',
-                'z54': '54 cm',
-                'z65': '65 cm'
-            }
-        },
-
-        poignees: {
-            label: 'Poignées',
-            data: {
-                'yes': 'Oui',
-                'no': 'Non',
-            }
-        },
-        largeur: {
-            label: 'Largeurs',
-            data: {
-                '40': '40 cm',
-                '60': '60 cm',
-                '70': '70 cm',
-                '90': '90 cm',
-                '105': '105 cm',
-                '120': '120 cm',
-                '124': '124 cm',
-                '140': '140 cm',
-                'surmesure': ' Sur-mesure'
-            }
-        },
-
-        vasque: {
-            // Simple vasque,Double vasque,Vasque à gauche,Vasque à poser
-            label: 'Vasques',
-            data: {
-                'VS': 'Simple vasque',
-                'VD': 'Double vasque',
-                'VG': 'Vasque à gauche',
-                'VP': 'Vasque à poser',
-            }
-        },
-
-
-        meuble: {
-            // Simple vasque,Double vasque,Vasque à gauche,Vasque à poser
-            label: 'Sous-vasques',
-            data: {
-                '1T': '1 tiroir',
-                '2T': '2 tiroirs',
-                '3T': '3 tiroirs',
-                'P': 'Porte',
-            }
-        },
-
-
-    }
-
-    $(function () {
-        var se = {};
-        var mem = {profondeur: [], poignees: [], largeur: [], vasque: [], meuble: []};
-        if (parent.fluidbook.discac !== undefined) {
-            mem = $.extend({}, mem, parent.fluidbook.discac);
-        }
-        $.each(selects, function (k, v) {
-            var allValues = [];
-            var s = '<select id="' + k + '" name="' + k + '" multiple>';
-            $.each(v.data, function (value, label) {
-                allValues.push(value);
-                var selected = mem[k].indexOf(value) >= 0 ? ' selected ' : '';
-                s += '<option value="' + value + '"' + selected + '>' + label + '</option>';
-            });
-            s += '</select>';
-            $("#selects").append(s);
-            se[k] = $("#" + k).multiSelect({
-                'noneText': v.label,
-                presets: [
-                    {
-                        name: 'Tout sélectionner',
-                        options: allValues,
-                    },
-                ]
-            }).on('change', function () {
-                updateChapters();
-            });
-        });
-
-        updateChapters();
-
-        function updateCheckboxes() {
-            $(":checkbox").checkbox();
-            $(":radio").checkbox();
-        }
-
-        function updateChapters() {
-            var save = {};
-            $('select').each(function () {
-                save[$(this).attr('name')] = $(this).val();
-            });
-            parent.fluidbook.discac = save;
-
-            var hasVisible = false;
-            $('nav a').each(function () {
-                var hide = false;
-                var a = $(this);
-                $("#selects select").each(function () {
-                    var vals = $(this).val();
-                    if (vals.length === 0) {
-                        return;
-                    }
-                    var name = $(this).attr('name');
-                    var attrvalues = $(a).attr('data-' + name).split(',');
-                    var inter = vals.filter(function (n) {
-                        return attrvalues.indexOf(n) !== -1;
-                    });
-                    if (inter.length === 0) {
-                        hide = true;
-                    }
-                });
-                if (hide) {
-                    $(a).hide();
-                } else {
-                    hasVisible = true;
-                    $(a).show();
-                }
-                updateCheckboxes();
-            });
-
-            if (hasVisible) {
-                $("#emptyres").hide();
-            } else {
-                $("#emptyres").show();
-            }
-
-        }
-
-        $(document).on('click', 'a', function () {
-            parent.location.hash = "#/page/" + $(this).data('page');
-            return false;
-        });
-
-        updateChapters();
-
-
-    });
-    (function ($) {
-        $.propHooks.checked = {
-            set: function (el, value) {
-                if (el.checked !== value) {
-                    el.checked = value;
-                    $(el).trigger('checkboxchange');
-                }
-            }
-        };
-
-        function JQcheckbox(element) {
-            this.element = element;
-            this.type = this.element.attr('type');
-            this.input = element;
-            this.substitute;
-            this.label = element.parents("label");
-            if (this.label.length == 0) {
-                this.label = $('label[for="' + element.attr('id') + '"]');
-            }
-            this.label.attr('data-value', element.attr('value'));
-            this.init();
-        }
-
-        JQcheckbox.prototype = {
-            init: function () {
-                var $this = this;
-                this.element.hide();
-                this.element.next(".checkbox").remove();
-                this.substitute = $('<div class="' + this.type + '"></div>');
-
-                var ignore = ['name', 'id', 'type', 'checked', 'style', 'value', 'class'];
-
-                $.each(this.element.attributes, function (k, v) {
-                    if (ignore == undefined || ignore.indexOf(k) == -1) {
-                        $this.substitute.attr(k, v);
-                    }
-                });
-                this.element.after(this.substitute);
-                this.initEvents();
-                this.initState();
-            },
-            initEvents: function () {
-                var $this = this;
-                this.substitute.off('click');
-                this.substitute.on('click', function () {
-                    $this.click();
-                    return false;
-                });
-
-                this.element.off('checkboxchange');
-                this.element.on('checkboxchange', function () {
-                    $this.initState();
-                });
-                this.label.off('click');
-                this.label.on('click', function (e) {
-                    if (e.target == this) {
-                        $this.click();
-                        return false;
-                    }
-                });
-
-            },
-            click: function () {
-                var change = false;
-                if (this.type == 'radio') {
-                    change = this.check();
-                } else {
-                    change = this.toggle();
-                }
-
-                if (change) {
-                    this.element.trigger('change');
-                }
-            },
-            initState: function () {
-                if (!this.element.prop('checked')) {
-                    this.uncheck();
-                } else {
-                    this.check();
-                }
-            },
-            toggle: function () {
-                this.element.trigger('change');
-                if (this.element.prop('checked')) {
-                    this.uncheck();
-                } else {
-                    this.check();
-                }
-                return true;
-            },
-            check: function () {
-                if (this.type == 'radio') {
-                    try {
-                        $(':radio[name="' + this.element.attr('name') + '"]').not(this.element).each(function () {
-                            $(this).data('checkbox').uncheck();
-                        });
-                    } catch (err) {
-
-                    }
-                }
-
-                if (this.substitute.attr('checked') != 'checked') {
-                    this.substitute.attr('checked', 'checked');
-                    this.element.prop('checked', true);
-                    this.label.attr('data-checked', 'checked');
-                    return true;
-                }
-                return false;
-            },
-            uncheck: function () {
-                this.label.attr('data-checked', null);
-                this.substitute.attr('checked', null);
-                this.element.prop('checked', false);
-            }
-
-
-        };
-
-        jQuery.fn.checkbox = function () {
-            return this.each(function () {
-                var $this = $(this);
-                if ($(this).data('checkbox') != undefined) {
-                    $(this).data('checkbox').initState();
-                    return;
-                }
-                $(this).data('checkbox', new JQcheckbox($this));
-            })
-        };
-    })(jQuery);
-
-</script>
-</body>
-</html>
\ No newline at end of file
diff --git a/Discac/jquery.multi-select.js b/Discac/jquery.multi-select.js
deleted file mode 100644 (file)
index 8143f30..0000000
+++ /dev/null
@@ -1,442 +0,0 @@
-// jquery.multi-select.js
-// by mySociety
-// https://github.com/mysociety/jquery-multi-select
-
-;(function ($) {
-
-    "use strict";
-
-    var pluginName = "multiSelect",
-        defaults = {
-            'containerHTML': '<div class="multi-select-container">',
-            'menuHTML': '<div class="multi-select-menu">',
-            'buttonHTML': '<span class="multi-select-button">',
-            'menuItemsHTML': '<div class="multi-select-menuitems">',
-            'menuItemHTML': '<label class="multi-select-menuitem">',
-            'presetsHTML': '<div class="multi-select-presets">',
-            'modalHTML': undefined,
-            'menuItemTitleClass': 'multi-select-menuitem--titled',
-            'activeClass': 'multi-select-container--open',
-            'noneText': '-- Select --',
-            'allText': undefined,
-            'presets': undefined,
-            'positionedMenuClass': 'multi-select-container--positioned',
-            'positionMenuWithin': undefined,
-            'viewportBottomGutter': 20,
-            'menuMinHeight': 200
-        };
-
-    /**
-     * @constructor
-     */
-    function MultiSelect(element, options) {
-        this.element = element;
-        this.$element = $(element);
-        this.settings = $.extend({}, defaults, options);
-        this._defaults = defaults;
-        this._name = pluginName;
-        this.init();
-    }
-
-    function arraysAreEqual(array1, array2) {
-        if (array1.length != array2.length) {
-            return false;
-        }
-
-        array1.sort();
-        array2.sort();
-
-        for (var i = 0; i < array1.length; i++) {
-            if (array1[i] !== array2[i]) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    $.extend(MultiSelect.prototype, {
-
-        init: function () {
-            this.checkSuitableInput();
-            this.findLabels();
-            this.constructContainer();
-            this.constructButton();
-            this.constructMenu();
-            this.constructModal();
-
-            this.setUpBodyClickListener();
-            this.setUpLabelsClickListener();
-
-            this.$element.hide();
-        },
-
-        checkSuitableInput: function (text) {
-            if (this.$element.is('select[multiple]') === false) {
-                throw new Error('$.multiSelect only works on <select multiple> elements');
-            }
-        },
-
-        findLabels: function () {
-            this.$labels = $('label[for="' + this.$element.attr('id') + '"]');
-        },
-
-        constructContainer: function () {
-            this.$container = $(this.settings['containerHTML']);
-            this.$element.data('multi-select-container', this.$container);
-            this.$container.insertAfter(this.$element);
-        },
-
-        constructButton: function () {
-            var _this = this;
-            this.$button = $(this.settings['buttonHTML']);
-            this.$button.attr({
-                'role': 'button',
-                'aria-haspopup': 'true',
-                'tabindex': 0,
-                'aria-label': this.$labels.eq(0).text()
-            })
-                .on('keydown.multiselect', function (e) {
-                    var key = e.which;
-                    var returnKey = 13;
-                    var escapeKey = 27;
-                    var spaceKey = 32;
-                    var downArrow = 40;
-                    if ((key === returnKey) || (key === spaceKey)) {
-                        e.preventDefault();
-                        _this.$button.click();
-                    } else if (key === downArrow) {
-                        e.preventDefault();
-                        _this.menuShow();
-                        var group = _this.$presets || _this.$menuItems;
-                        group.children(":first").focus();
-                    } else if (key === escapeKey) {
-                        _this.menuHide();
-                    }
-                }).on('click.multiselect', function (e) {
-                _this.menuToggle();
-            })
-                .appendTo(this.$container);
-
-            this.$element.on('change.multiselect', function () {
-                _this.updateButtonContents();
-            });
-
-            this.updateButtonContents();
-        },
-
-        updateButtonContents: function () {
-            var _this = this;
-            var options = [];
-            var selected = [];
-
-            this.$element.find('option').each(function () {
-                var text = /** @type string */ ($(this).text());
-                options.push(text);
-                if ($(this).is(':selected')) {
-                    selected.push($.trim(text));
-                }
-            });
-
-            this.$button.empty();
-
-            if (selected.length == 0) {
-                this.$button.removeClass('active').text(this.settings['noneText']);
-            } else if ((selected.length === options.length) && this.settings['allText']) {
-                this.$button.removeClass('active').text(this.settings['allText']);
-            } else {
-                this.$button.addClass('active').text(this.settings['noneText'] + ' : ' + selected.join(', '));
-            }
-        },
-
-        constructMenu: function () {
-            var _this = this;
-
-            this.$menu = $(this.settings['menuHTML']);
-            this.$menu.attr({
-                'role': 'menu'
-            }).on('keyup.multiselect', function (e) {
-                var key = e.which;
-                var escapeKey = 27;
-                if (key === escapeKey) {
-                    _this.menuHide();
-                    _this.$button.focus();
-                }
-            })
-                .appendTo(this.$container);
-
-            this.constructMenuItems();
-
-            if (this.settings['presets']) {
-                this.constructPresets();
-            }
-        },
-
-        constructMenuItems: function () {
-            var _this = this;
-
-            this.$menuItems = $(this.settings['menuItemsHTML']);
-            this.$menu.append(this.$menuItems);
-
-            this.$element.on('change.multiselect', function (e, internal) {
-                // Don't need to update the menu items if this
-                // change event was fired by our tickbox handler.
-                if (internal !== true) {
-                    _this.updateMenuItems();
-                }
-            });
-
-            this.updateMenuItems();
-        },
-
-        updateMenuItems: function () {
-            var _this = this;
-            this.$menuItems.empty();
-
-            this.$element.children('optgroup,option').each(function (index, element) {
-                var $item;
-                if (element.nodeName === 'OPTION') {
-                    $item = _this.constructMenuItem($(element), index);
-                    _this.$menuItems.append($item);
-                } else {
-                    _this.constructMenuItemsGroup($(element), index);
-                }
-            });
-        },
-
-        upDown: function (type, e) {
-            var key = e.which;
-            var upArrow = 38;
-            var downArrow = 40;
-
-            if (key === upArrow) {
-                e.preventDefault();
-                var prev = $(e.currentTarget).prev();
-                if (prev.length) {
-                    prev.focus();
-                } else if (this.$presets && type === 'menuitem') {
-                    this.$presets.children(':last').focus();
-                } else {
-                    this.$button.focus();
-                }
-            } else if (key === downArrow) {
-                e.preventDefault();
-                var next = $(e.currentTarget).next();
-                if (next.length || type === 'menuitem') {
-                    next.focus();
-                } else {
-                    this.$menuItems.children(':first').focus();
-                }
-            }
-        },
-
-        constructPresets: function () {
-            var _this = this;
-            this.$presets = $(this.settings['presetsHTML']);
-            this.$menu.append(this.$presets);
-
-            $.each(this.settings['presets'], function (i, preset) {
-                var unique_id = _this.$element.attr('name') + '_preset_' + i;
-                var $item = $(_this.settings['menuItemHTML'])
-                    .attr({
-                        'for': unique_id,
-                        'role': 'menuitem'
-                    })
-                    .text(' ' + preset.name)
-                    .on('keydown.multiselect', _this.upDown.bind(_this, 'preset'))
-                    .appendTo(_this.$presets);
-
-                var $input = $('<input>')
-                    .attr({
-                        'type': 'checkbox',
-                        'name': _this.$element.attr('name') + '_presets',
-                        'id': unique_id
-                    })
-                    .prependTo($item);
-
-                $input.on('change.multiselect', function () {
-                    if ($(this).prop('checked')) {
-                        _this.$element.val(preset.options);
-                    } else {
-                        _this.$element.val([]);
-                    }
-                    _this.$element.trigger('change');
-                });
-            });
-            _this.$presets.append('<button>Valider</button>');
-            this.$presets.on('click','button',function(){
-                _this.menuHide();
-                return false;
-            });
-
-            this.$element.on('change.multiselect', function () {
-                _this.updatePresets();
-            });
-
-            this.updatePresets();
-        },
-
-        updatePresets: function () {
-            var _this = this;
-
-            $.each(this.settings['presets'], function (i, preset) {
-                var unique_id = _this.$element.attr('name') + '_preset_' + i;
-                var $input = _this.$presets.find('#' + unique_id);
-
-                if (arraysAreEqual(preset.options || [], _this.$element.val() || [])) {
-                    $input.prop('checked', true);
-                } else {
-                    $input.prop('checked', false);
-                }
-            });
-        },
-
-        constructMenuItemsGroup: function ($optgroup, optgroup_index) {
-            var _this = this;
-
-            $optgroup.children('option').each(function (option_index, option) {
-                var $item = _this.constructMenuItem($(option), optgroup_index + '_' + option_index);
-                var cls = _this.settings['menuItemTitleClass'];
-                if (option_index !== 0) {
-                    cls += 'sr';
-                }
-                $item.addClass(cls).attr('data-group-title', $optgroup.attr('label'));
-                _this.$menuItems.append($item);
-            });
-        },
-
-        constructMenuItem: function ($option, option_index) {
-            var unique_id = this.$element.attr('name') + '_' + option_index;
-            var $item = $(this.settings['menuItemHTML'])
-                .attr({
-                    'for': unique_id,
-                    'role': 'menuitem'
-                })
-                .on('keydown.multiselect', this.upDown.bind(this, 'menuitem'))
-                .text(' ' + $option.text());
-
-            var $input = $('<input>')
-                .attr({
-                    'type': 'checkbox',
-                    'id': unique_id,
-                    'value': $option.val()
-                })
-                .prependTo($item);
-
-            if ($option.is(':disabled')) {
-                $input.attr('disabled', 'disabled');
-            }
-            if ($option.is(':selected')) {
-                $input.prop('checked', 'checked');
-            }
-
-            $input.on('change.multiselect', function () {
-                if ($(this).prop('checked')) {
-                    $option.prop('selected', true);
-                } else {
-                    $option.prop('selected', false);
-                }
-
-                // .prop() on its own doesn't generate a change event.
-                // Other plugins might want to do stuff onChange.
-                $option.trigger('change', [true]);
-            });
-
-            return $item;
-        },
-
-        constructModal: function () {
-            var _this = this;
-
-            if (this.settings['modalHTML']) {
-                this.$modal = $(this.settings['modalHTML']);
-                this.$modal.on('click.multiselect', function () {
-                    _this.menuHide();
-                })
-                this.$modal.insertBefore(this.$menu);
-            }
-        },
-
-        setUpBodyClickListener: function () {
-            var _this = this;
-
-            // Hide the $menu when you click outside of it.
-            $('html').on('click.multiselect', function () {
-                _this.menuHide();
-            });
-
-            // Stop click events from inside the $button or $menu from
-            // bubbling up to the body and closing the menu!
-            this.$container.on('click.multiselect', function (e) {
-                e.stopPropagation();
-            });
-        },
-
-        setUpLabelsClickListener: function () {
-            var _this = this;
-            this.$labels.on('click.multiselect', function (e) {
-                e.preventDefault();
-                e.stopPropagation();
-                _this.menuToggle();
-            });
-        },
-
-        menuShow: function () {
-            $('html').trigger('click.multiselect'); // Close any other open menus
-            this.$container.addClass(this.settings['activeClass']);
-
-            if (this.settings['positionMenuWithin'] && this.settings['positionMenuWithin'] instanceof $) {
-                var menuLeftEdge = this.$menu.offset().left + this.$menu.outerWidth();
-                var withinLeftEdge = this.settings['positionMenuWithin'].offset().left +
-                    this.settings['positionMenuWithin'].outerWidth();
-
-                if (menuLeftEdge > withinLeftEdge) {
-                    this.$menu.css('width', (withinLeftEdge - this.$menu.offset().left));
-                    this.$container.addClass(this.settings['positionedMenuClass']);
-                }
-            }
-
-            var menuBottom = this.$menu.offset().top + this.$menu.outerHeight();
-            var viewportBottom = $(window).scrollTop() + $(window).height();
-            if (menuBottom > viewportBottom - this.settings['viewportBottomGutter']) {
-                this.$menu.css({
-                    'maxHeight': Math.max(
-                        viewportBottom - this.settings['viewportBottomGutter'] - this.$menu.offset().top,
-                        this.settings['menuMinHeight']
-                    ),
-                    'overflow': 'scroll'
-                });
-            } else {
-                this.$menu.css({
-                    'maxHeight': '',
-                    'overflow': ''
-                });
-            }
-        },
-
-        menuHide: function () {
-            this.$container.removeClass(this.settings['activeClass']);
-            this.$container.removeClass(this.settings['positionedMenuClass']);
-            this.$menu.css('width', 'auto');
-        },
-
-        menuToggle: function () {
-            if (this.$container.hasClass(this.settings['activeClass'])) {
-                this.menuHide();
-            } else {
-                this.menuShow();
-            }
-        }
-
-    });
-
-    $.fn[pluginName] = function (options) {
-        return this.each(function () {
-            if (!$.data(this, "plugin_" + pluginName)) {
-                $.data(this, "plugin_" + pluginName,
-                    new MultiSelect(this, options));
-            }
-        });
-    };
-
-})(jQuery);
diff --git a/Michelin/Michelin.zip b/Michelin/Michelin.zip
new file mode 100644 (file)
index 0000000..d9a757d
Binary files /dev/null and b/Michelin/Michelin.zip differ
diff --git a/Michelin/_doc/publish.bat b/Michelin/_doc/publish.bat
new file mode 100644 (file)
index 0000000..5ac2fa5
--- /dev/null
@@ -0,0 +1,3 @@
+@echo off
+"C:\Program Files\7-Zip\7z.exe" a -tzip ../Michelin.zip ../ -xr0!_doc -xr0!*.zip -xr0!*.less -xr0!*.map
+scp -P 51895 ../Michelin.zip extranet@workshop.fluidbook.com:/application/fluidbook/books/working/20687
\ No newline at end of file
diff --git a/Michelin/checkbox.png b/Michelin/checkbox.png
new file mode 100644 (file)
index 0000000..9163bea
Binary files /dev/null and b/Michelin/checkbox.png differ
diff --git a/Michelin/index.html b/Michelin/index.html
new file mode 100644 (file)
index 0000000..f8e256b
--- /dev/null
@@ -0,0 +1,512 @@
+<!DOCTYPE html>
+<html lang="fr">
+<head>
+    <meta charset="UTF-8">
+    <meta name="width" content="440">
+    <meta name="height" content="760">
+    <title></title>
+    <link rel="preconnect" href="https://fonts.gstatic.com">
+    <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400&display=swap" rel="stylesheet">
+    <style>
+        * {
+            padding: 0;
+            margin: 0;
+        }
+
+        html, body {
+            height: 100%;
+        }
+
+        body {
+            font-family: "Open Sans", sans-serif;
+            font-size: 17px;
+            color: #fff;
+            background-color: #93c23a;
+        }
+
+        a {
+            color: #fff;
+            text-decoration: none;
+            display: block;
+            background-color: #93c23a;
+            padding: 5px 25px;
+            position: relative;
+        }
+
+        a:hover {
+            background-color: #66a11a;
+            transition: background-color 200ms;
+        }
+
+        a span {
+            position: absolute;
+            right: 25px;
+            top: 5px;
+        }
+
+        h2 {
+            padding: 25px 25px 0px;
+            font-weight: 400;
+            font-size: 17px;
+        }
+
+        main {
+            max-width: 440px;
+            margin: 0 auto;
+        }
+
+        #selects {
+            padding: 25px;
+        }
+
+        .multi-select-container {
+            display: block;
+            position: relative;
+        }
+
+        .multi-select-menu {
+            position: absolute;
+            left: 0;
+            top: 0.8em;
+            float: left;
+            min-width: 100%;
+            background: #fff;
+            margin: 1em 0;
+            padding: 0.4em 0;
+            display: none;
+            z-index: 2;
+        }
+
+
+        .multi-select-menu label {
+            color: #464646;
+            display: block;
+            padding: 3px 10px;
+            font-size: 16px;
+        }
+
+        .multi-select-menu input {
+            margin-right: 0.3em;
+            vertical-align: 0.1em;
+        }
+
+        .multi-select-button {
+            margin: 4px 0;
+            display: block;
+            color: #474747;
+            font-size: 0.875em;
+            padding: 0.2em 2em 0.2em 0.6em;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            background-color: #fff;
+            border-radius: 4px;
+            cursor: default;
+            position: relative;
+        }
+
+        .multi-select-button.active {
+            background-color: #66a11a;
+            color: #fff;
+        }
+
+        .multi-select-button.active:after {
+            border-color: #fff transparent transparent transparent;
+        }
+
+        .multi-select-container--open .multi-select-button.active::after {
+            border-color: transparent transparent #fff transparent
+        }
+
+        .multi-select-button:after {
+            content: "";
+            display: inline-block;
+            width: 0;
+            height: 0;
+            border-style: solid;
+            border-width: 0.4em 0.4em 0 0.4em;
+            border-color: #474747 transparent transparent transparent;
+            margin-left: 0.4em;
+            vertical-align: 0.1em;
+            position: absolute;
+            right: 10px;
+            top: 11px;
+        }
+
+        .multi-select-presets {
+            position: relative;
+            padding-top: 5px;
+            border-top: 1px solid #eee;
+        }
+
+        .multi-select-presets button {
+            cursor: pointer;
+            position: absolute;
+            top: 5px;
+            right: 5px;
+            background-color: #66a11a;
+            color: #fff;
+            text-transform: uppercase;
+            border: 0;
+            border-radius: 4px;
+            padding: 2px 15px;
+            font-size: 17px;
+            font-family: "Open Sans", sans-serif;
+        }
+
+        .multi-select-container--open .multi-select-menu {
+            display: block;
+        }
+
+        .multi-select-container--open .multi-select-button:after {
+            border-width: 0 0.4em 0.4em 0.4em;
+            border-color: transparent transparent #999 transparent;
+        }
+
+        .checkbox {
+            display: inline-block;
+            width: 19px;
+            height: 19px;
+            background-image: url("checkbox.png");
+            background-size: 19px auto;
+            background-repeat: no-repeat;
+            background-position: 0 100%;
+            margin-right: 5px;
+            vertical-align: top;
+            position: relative;
+            top: 1px;
+        }
+
+        .checkbox[checked] {
+            background-position: 0 0;
+        }
+
+        #emptyres {
+            padding: 0 25px;
+        }
+
+    </style>
+</head>
+<body>
+<main>
+    <h2>Filtrer ma recherche</h2>
+    <div id="selects">
+
+    </div>
+    <nav>
+        <a href="#" data-largeur="70,90,105,140" data-vasque="VS,VD,VG,VP" data-meuble="1T,2T,P" data-profondeur="z54"
+           data-poignees="yes" data-page="6">Loft<span>6</span></a>
+        <a href="#" data-largeur="70,90,140" data-vasque="VS,VD,VP" data-meuble="2T" data-profondeur="z54"
+           data-poignees="no" data-page="14">Allure<span>14</span></a>
+        <a href="#" data-largeur="70,90,140" data-vasque="VS,VD,VP" data-meuble="3T" data-profondeur="z54"
+           data-poignees="yes" data-page="20">Trendy<span>20</span></a>
+        <a href="#" data-largeur="60,80,90,120" data-vasque="VS,VD,VG,VP" data-meuble="1T,2T" data-profondeur="z46"
+           data-poignees="no" data-page="26">Rivage<span>26</span></a>
+        <a href="#" data-largeur="90,120" data-vasque="VS,VD,VG" data-meuble="2T" data-profondeur="z46"
+           data-poignees="no" data-page="36">Esquisse<span>36</span></a>
+        <a href="#" data-largeur="60,80,90,120" data-vasque="VS,VD,VG,VP" data-meuble="1T,2T,P" data-profondeur="z46"
+           data-poignees="yes" data-page="42">Swing<span>42</span></a>
+        <a href="#" data-largeur="90,120" data-vasque="VS,VD,VP" data-meuble="1T,2T" data-profondeur="z46"
+           data-poignees="yes" data-page="54">Equilibre<span>54</span></a>
+        <a href="#" data-largeur="90,120" data-vasque="VS,VD,VP" data-meuble="1T,2T" data-profondeur="z46"
+           data-poignees="yes" data-page="60">Fabrik<span>60</span></a>
+        <a href="#" data-largeur="60,80,90,120,surmesure" data-vasque="VS,VD,VG,VP" data-meuble="1T,2T,P"
+           data-profondeur="z46" data-poignees="yes" data-page="66">Osmose<span>66</span></a>
+        <a href="#" data-largeur="60,70,80,90,120" data-vasque="VS,VD" data-meuble="2T,P" data-profondeur="z46"
+           data-poignees="yes" data-page="74">Chango<span>74</span></a>
+        <a href="#" data-largeur="60,8" data-vasque="VS" data-meuble="P" data-profondeur="z36"
+           data-poignees="yes" data-page="80">Lindy<span>80</span></a>
+        <a href="#" data-largeur="40" data-vasque="VS" data-meuble="P" data-profondeur="z22" data-poignees="yes"
+           data-page="84">Lave-mains<span>84</span></a>
+        <a href="#" data-largeur="80" data-vasque="VS" data-meuble="P" data-profondeur="z54"
+           data-poignees="yes" data-page="86">Estia<span>86</span></a>
+        <a href="#" data-largeur="124" data-vasque="VG" data-meuble="2T,P" data-profondeur="z65" data-poignees="yes"
+           data-page="88">Lave-linge<span>88</span></a>
+
+
+    </nav>
+    <div id="emptyres">Aucun produit ne correspond aux filtres sélectionnés</div>
+</main>
+<script src="../../jquery.js"></script>
+<script src="jquery.multi-select.js"></script>
+<script>
+    var selects = {
+        profondeur: {
+            label: 'Profondeurs',
+            data: {
+                'z22': '22 cm',
+                'z36': '36 cm',
+                'z46': '46 cm',
+                'z54': '54 cm',
+                'z65': '65 cm'
+            }
+        },
+
+        poignees: {
+            label: 'Poignées',
+            data: {
+                'yes': 'Oui',
+                'no': 'Non',
+            }
+        },
+        largeur: {
+            label: 'Largeurs',
+            data: {
+                '40': '40 cm',
+                '60': '60 cm',
+                '70': '70 cm',
+                '90': '90 cm',
+                '105': '105 cm',
+                '120': '120 cm',
+                '124': '124 cm',
+                '140': '140 cm',
+                'surmesure': ' Sur-mesure'
+            }
+        },
+
+        vasque: {
+            // Simple vasque,Double vasque,Vasque à gauche,Vasque à poser
+            label: 'Vasques',
+            data: {
+                'VS': 'Simple vasque',
+                'VD': 'Double vasque',
+                'VG': 'Vasque à gauche',
+                'VP': 'Vasque à poser',
+            }
+        },
+
+
+        meuble: {
+            // Simple vasque,Double vasque,Vasque à gauche,Vasque à poser
+            label: 'Sous-vasques',
+            data: {
+                '1T': '1 tiroir',
+                '2T': '2 tiroirs',
+                '3T': '3 tiroirs',
+                'P': 'Porte',
+            }
+        },
+
+
+    }
+
+    $(function () {
+        var se = {};
+        var mem = {profondeur: [], poignees: [], largeur: [], vasque: [], meuble: []};
+        if (parent.fluidbook.discac !== undefined) {
+            mem = $.extend({}, mem, parent.fluidbook.discac);
+        }
+        $.each(selects, function (k, v) {
+            var allValues = [];
+            var s = '<select id="' + k + '" name="' + k + '" multiple>';
+            $.each(v.data, function (value, label) {
+                allValues.push(value);
+                var selected = mem[k].indexOf(value) >= 0 ? ' selected ' : '';
+                s += '<option value="' + value + '"' + selected + '>' + label + '</option>';
+            });
+            s += '</select>';
+            $("#selects").append(s);
+            se[k] = $("#" + k).multiSelect({
+                'noneText': v.label,
+                presets: [
+                    {
+                        name: 'Tout sélectionner',
+                        options: allValues,
+                    },
+                ]
+            }).on('change', function () {
+                updateChapters();
+            });
+        });
+
+        updateChapters();
+
+        function updateCheckboxes() {
+            $(":checkbox").checkbox();
+            $(":radio").checkbox();
+        }
+
+        function updateChapters() {
+            var save = {};
+            $('select').each(function () {
+                save[$(this).attr('name')] = $(this).val();
+            });
+            parent.fluidbook.discac = save;
+
+            var hasVisible = false;
+            $('nav a').each(function () {
+                var hide = false;
+                var a = $(this);
+                $("#selects select").each(function () {
+                    var vals = $(this).val();
+                    if (vals.length === 0) {
+                        return;
+                    }
+                    var name = $(this).attr('name');
+                    var attrvalues = $(a).attr('data-' + name).split(',');
+                    var inter = vals.filter(function (n) {
+                        return attrvalues.indexOf(n) !== -1;
+                    });
+                    if (inter.length === 0) {
+                        hide = true;
+                    }
+                });
+                if (hide) {
+                    $(a).hide();
+                } else {
+                    hasVisible = true;
+                    $(a).show();
+                }
+                updateCheckboxes();
+            });
+
+            if (hasVisible) {
+                $("#emptyres").hide();
+            } else {
+                $("#emptyres").show();
+            }
+
+        }
+
+        $(document).on('click', 'a', function () {
+            parent.location.hash = "#/page/" + $(this).data('page');
+            return false;
+        });
+
+        updateChapters();
+
+
+    });
+    (function ($) {
+        $.propHooks.checked = {
+            set: function (el, value) {
+                if (el.checked !== value) {
+                    el.checked = value;
+                    $(el).trigger('checkboxchange');
+                }
+            }
+        };
+
+        function JQcheckbox(element) {
+            this.element = element;
+            this.type = this.element.attr('type');
+            this.input = element;
+            this.substitute;
+            this.label = element.parents("label");
+            if (this.label.length == 0) {
+                this.label = $('label[for="' + element.attr('id') + '"]');
+            }
+            this.label.attr('data-value', element.attr('value'));
+            this.init();
+        }
+
+        JQcheckbox.prototype = {
+            init: function () {
+                var $this = this;
+                this.element.hide();
+                this.element.next(".checkbox").remove();
+                this.substitute = $('<div class="' + this.type + '"></div>');
+
+                var ignore = ['name', 'id', 'type', 'checked', 'style', 'value', 'class'];
+
+                $.each(this.element.attributes, function (k, v) {
+                    if (ignore == undefined || ignore.indexOf(k) == -1) {
+                        $this.substitute.attr(k, v);
+                    }
+                });
+                this.element.after(this.substitute);
+                this.initEvents();
+                this.initState();
+            },
+            initEvents: function () {
+                var $this = this;
+                this.substitute.off('click');
+                this.substitute.on('click', function () {
+                    $this.click();
+                    return false;
+                });
+
+                this.element.off('checkboxchange');
+                this.element.on('checkboxchange', function () {
+                    $this.initState();
+                });
+                this.label.off('click');
+                this.label.on('click', function (e) {
+                    if (e.target == this) {
+                        $this.click();
+                        return false;
+                    }
+                });
+
+            },
+            click: function () {
+                var change = false;
+                if (this.type == 'radio') {
+                    change = this.check();
+                } else {
+                    change = this.toggle();
+                }
+
+                if (change) {
+                    this.element.trigger('change');
+                }
+            },
+            initState: function () {
+                if (!this.element.prop('checked')) {
+                    this.uncheck();
+                } else {
+                    this.check();
+                }
+            },
+            toggle: function () {
+                this.element.trigger('change');
+                if (this.element.prop('checked')) {
+                    this.uncheck();
+                } else {
+                    this.check();
+                }
+                return true;
+            },
+            check: function () {
+                if (this.type == 'radio') {
+                    try {
+                        $(':radio[name="' + this.element.attr('name') + '"]').not(this.element).each(function () {
+                            $(this).data('checkbox').uncheck();
+                        });
+                    } catch (err) {
+
+                    }
+                }
+
+                if (this.substitute.attr('checked') != 'checked') {
+                    this.substitute.attr('checked', 'checked');
+                    this.element.prop('checked', true);
+                    this.label.attr('data-checked', 'checked');
+                    return true;
+                }
+                return false;
+            },
+            uncheck: function () {
+                this.label.attr('data-checked', null);
+                this.substitute.attr('checked', null);
+                this.element.prop('checked', false);
+            }
+
+
+        };
+
+        jQuery.fn.checkbox = function () {
+            return this.each(function () {
+                var $this = $(this);
+                if ($(this).data('checkbox') != undefined) {
+                    $(this).data('checkbox').initState();
+                    return;
+                }
+                $(this).data('checkbox', new JQcheckbox($this));
+            })
+        };
+    })(jQuery);
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/Michelin/jquery.multi-select.js b/Michelin/jquery.multi-select.js
new file mode 100644 (file)
index 0000000..8143f30
--- /dev/null
@@ -0,0 +1,442 @@
+// jquery.multi-select.js
+// by mySociety
+// https://github.com/mysociety/jquery-multi-select
+
+;(function ($) {
+
+    "use strict";
+
+    var pluginName = "multiSelect",
+        defaults = {
+            'containerHTML': '<div class="multi-select-container">',
+            'menuHTML': '<div class="multi-select-menu">',
+            'buttonHTML': '<span class="multi-select-button">',
+            'menuItemsHTML': '<div class="multi-select-menuitems">',
+            'menuItemHTML': '<label class="multi-select-menuitem">',
+            'presetsHTML': '<div class="multi-select-presets">',
+            'modalHTML': undefined,
+            'menuItemTitleClass': 'multi-select-menuitem--titled',
+            'activeClass': 'multi-select-container--open',
+            'noneText': '-- Select --',
+            'allText': undefined,
+            'presets': undefined,
+            'positionedMenuClass': 'multi-select-container--positioned',
+            'positionMenuWithin': undefined,
+            'viewportBottomGutter': 20,
+            'menuMinHeight': 200
+        };
+
+    /**
+     * @constructor
+     */
+    function MultiSelect(element, options) {
+        this.element = element;
+        this.$element = $(element);
+        this.settings = $.extend({}, defaults, options);
+        this._defaults = defaults;
+        this._name = pluginName;
+        this.init();
+    }
+
+    function arraysAreEqual(array1, array2) {
+        if (array1.length != array2.length) {
+            return false;
+        }
+
+        array1.sort();
+        array2.sort();
+
+        for (var i = 0; i < array1.length; i++) {
+            if (array1[i] !== array2[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    $.extend(MultiSelect.prototype, {
+
+        init: function () {
+            this.checkSuitableInput();
+            this.findLabels();
+            this.constructContainer();
+            this.constructButton();
+            this.constructMenu();
+            this.constructModal();
+
+            this.setUpBodyClickListener();
+            this.setUpLabelsClickListener();
+
+            this.$element.hide();
+        },
+
+        checkSuitableInput: function (text) {
+            if (this.$element.is('select[multiple]') === false) {
+                throw new Error('$.multiSelect only works on <select multiple> elements');
+            }
+        },
+
+        findLabels: function () {
+            this.$labels = $('label[for="' + this.$element.attr('id') + '"]');
+        },
+
+        constructContainer: function () {
+            this.$container = $(this.settings['containerHTML']);
+            this.$element.data('multi-select-container', this.$container);
+            this.$container.insertAfter(this.$element);
+        },
+
+        constructButton: function () {
+            var _this = this;
+            this.$button = $(this.settings['buttonHTML']);
+            this.$button.attr({
+                'role': 'button',
+                'aria-haspopup': 'true',
+                'tabindex': 0,
+                'aria-label': this.$labels.eq(0).text()
+            })
+                .on('keydown.multiselect', function (e) {
+                    var key = e.which;
+                    var returnKey = 13;
+                    var escapeKey = 27;
+                    var spaceKey = 32;
+                    var downArrow = 40;
+                    if ((key === returnKey) || (key === spaceKey)) {
+                        e.preventDefault();
+                        _this.$button.click();
+                    } else if (key === downArrow) {
+                        e.preventDefault();
+                        _this.menuShow();
+                        var group = _this.$presets || _this.$menuItems;
+                        group.children(":first").focus();
+                    } else if (key === escapeKey) {
+                        _this.menuHide();
+                    }
+                }).on('click.multiselect', function (e) {
+                _this.menuToggle();
+            })
+                .appendTo(this.$container);
+
+            this.$element.on('change.multiselect', function () {
+                _this.updateButtonContents();
+            });
+
+            this.updateButtonContents();
+        },
+
+        updateButtonContents: function () {
+            var _this = this;
+            var options = [];
+            var selected = [];
+
+            this.$element.find('option').each(function () {
+                var text = /** @type string */ ($(this).text());
+                options.push(text);
+                if ($(this).is(':selected')) {
+                    selected.push($.trim(text));
+                }
+            });
+
+            this.$button.empty();
+
+            if (selected.length == 0) {
+                this.$button.removeClass('active').text(this.settings['noneText']);
+            } else if ((selected.length === options.length) && this.settings['allText']) {
+                this.$button.removeClass('active').text(this.settings['allText']);
+            } else {
+                this.$button.addClass('active').text(this.settings['noneText'] + ' : ' + selected.join(', '));
+            }
+        },
+
+        constructMenu: function () {
+            var _this = this;
+
+            this.$menu = $(this.settings['menuHTML']);
+            this.$menu.attr({
+                'role': 'menu'
+            }).on('keyup.multiselect', function (e) {
+                var key = e.which;
+                var escapeKey = 27;
+                if (key === escapeKey) {
+                    _this.menuHide();
+                    _this.$button.focus();
+                }
+            })
+                .appendTo(this.$container);
+
+            this.constructMenuItems();
+
+            if (this.settings['presets']) {
+                this.constructPresets();
+            }
+        },
+
+        constructMenuItems: function () {
+            var _this = this;
+
+            this.$menuItems = $(this.settings['menuItemsHTML']);
+            this.$menu.append(this.$menuItems);
+
+            this.$element.on('change.multiselect', function (e, internal) {
+                // Don't need to update the menu items if this
+                // change event was fired by our tickbox handler.
+                if (internal !== true) {
+                    _this.updateMenuItems();
+                }
+            });
+
+            this.updateMenuItems();
+        },
+
+        updateMenuItems: function () {
+            var _this = this;
+            this.$menuItems.empty();
+
+            this.$element.children('optgroup,option').each(function (index, element) {
+                var $item;
+                if (element.nodeName === 'OPTION') {
+                    $item = _this.constructMenuItem($(element), index);
+                    _this.$menuItems.append($item);
+                } else {
+                    _this.constructMenuItemsGroup($(element), index);
+                }
+            });
+        },
+
+        upDown: function (type, e) {
+            var key = e.which;
+            var upArrow = 38;
+            var downArrow = 40;
+
+            if (key === upArrow) {
+                e.preventDefault();
+                var prev = $(e.currentTarget).prev();
+                if (prev.length) {
+                    prev.focus();
+                } else if (this.$presets && type === 'menuitem') {
+                    this.$presets.children(':last').focus();
+                } else {
+                    this.$button.focus();
+                }
+            } else if (key === downArrow) {
+                e.preventDefault();
+                var next = $(e.currentTarget).next();
+                if (next.length || type === 'menuitem') {
+                    next.focus();
+                } else {
+                    this.$menuItems.children(':first').focus();
+                }
+            }
+        },
+
+        constructPresets: function () {
+            var _this = this;
+            this.$presets = $(this.settings['presetsHTML']);
+            this.$menu.append(this.$presets);
+
+            $.each(this.settings['presets'], function (i, preset) {
+                var unique_id = _this.$element.attr('name') + '_preset_' + i;
+                var $item = $(_this.settings['menuItemHTML'])
+                    .attr({
+                        'for': unique_id,
+                        'role': 'menuitem'
+                    })
+                    .text(' ' + preset.name)
+                    .on('keydown.multiselect', _this.upDown.bind(_this, 'preset'))
+                    .appendTo(_this.$presets);
+
+                var $input = $('<input>')
+                    .attr({
+                        'type': 'checkbox',
+                        'name': _this.$element.attr('name') + '_presets',
+                        'id': unique_id
+                    })
+                    .prependTo($item);
+
+                $input.on('change.multiselect', function () {
+                    if ($(this).prop('checked')) {
+                        _this.$element.val(preset.options);
+                    } else {
+                        _this.$element.val([]);
+                    }
+                    _this.$element.trigger('change');
+                });
+            });
+            _this.$presets.append('<button>Valider</button>');
+            this.$presets.on('click','button',function(){
+                _this.menuHide();
+                return false;
+            });
+
+            this.$element.on('change.multiselect', function () {
+                _this.updatePresets();
+            });
+
+            this.updatePresets();
+        },
+
+        updatePresets: function () {
+            var _this = this;
+
+            $.each(this.settings['presets'], function (i, preset) {
+                var unique_id = _this.$element.attr('name') + '_preset_' + i;
+                var $input = _this.$presets.find('#' + unique_id);
+
+                if (arraysAreEqual(preset.options || [], _this.$element.val() || [])) {
+                    $input.prop('checked', true);
+                } else {
+                    $input.prop('checked', false);
+                }
+            });
+        },
+
+        constructMenuItemsGroup: function ($optgroup, optgroup_index) {
+            var _this = this;
+
+            $optgroup.children('option').each(function (option_index, option) {
+                var $item = _this.constructMenuItem($(option), optgroup_index + '_' + option_index);
+                var cls = _this.settings['menuItemTitleClass'];
+                if (option_index !== 0) {
+                    cls += 'sr';
+                }
+                $item.addClass(cls).attr('data-group-title', $optgroup.attr('label'));
+                _this.$menuItems.append($item);
+            });
+        },
+
+        constructMenuItem: function ($option, option_index) {
+            var unique_id = this.$element.attr('name') + '_' + option_index;
+            var $item = $(this.settings['menuItemHTML'])
+                .attr({
+                    'for': unique_id,
+                    'role': 'menuitem'
+                })
+                .on('keydown.multiselect', this.upDown.bind(this, 'menuitem'))
+                .text(' ' + $option.text());
+
+            var $input = $('<input>')
+                .attr({
+                    'type': 'checkbox',
+                    'id': unique_id,
+                    'value': $option.val()
+                })
+                .prependTo($item);
+
+            if ($option.is(':disabled')) {
+                $input.attr('disabled', 'disabled');
+            }
+            if ($option.is(':selected')) {
+                $input.prop('checked', 'checked');
+            }
+
+            $input.on('change.multiselect', function () {
+                if ($(this).prop('checked')) {
+                    $option.prop('selected', true);
+                } else {
+                    $option.prop('selected', false);
+                }
+
+                // .prop() on its own doesn't generate a change event.
+                // Other plugins might want to do stuff onChange.
+                $option.trigger('change', [true]);
+            });
+
+            return $item;
+        },
+
+        constructModal: function () {
+            var _this = this;
+
+            if (this.settings['modalHTML']) {
+                this.$modal = $(this.settings['modalHTML']);
+                this.$modal.on('click.multiselect', function () {
+                    _this.menuHide();
+                })
+                this.$modal.insertBefore(this.$menu);
+            }
+        },
+
+        setUpBodyClickListener: function () {
+            var _this = this;
+
+            // Hide the $menu when you click outside of it.
+            $('html').on('click.multiselect', function () {
+                _this.menuHide();
+            });
+
+            // Stop click events from inside the $button or $menu from
+            // bubbling up to the body and closing the menu!
+            this.$container.on('click.multiselect', function (e) {
+                e.stopPropagation();
+            });
+        },
+
+        setUpLabelsClickListener: function () {
+            var _this = this;
+            this.$labels.on('click.multiselect', function (e) {
+                e.preventDefault();
+                e.stopPropagation();
+                _this.menuToggle();
+            });
+        },
+
+        menuShow: function () {
+            $('html').trigger('click.multiselect'); // Close any other open menus
+            this.$container.addClass(this.settings['activeClass']);
+
+            if (this.settings['positionMenuWithin'] && this.settings['positionMenuWithin'] instanceof $) {
+                var menuLeftEdge = this.$menu.offset().left + this.$menu.outerWidth();
+                var withinLeftEdge = this.settings['positionMenuWithin'].offset().left +
+                    this.settings['positionMenuWithin'].outerWidth();
+
+                if (menuLeftEdge > withinLeftEdge) {
+                    this.$menu.css('width', (withinLeftEdge - this.$menu.offset().left));
+                    this.$container.addClass(this.settings['positionedMenuClass']);
+                }
+            }
+
+            var menuBottom = this.$menu.offset().top + this.$menu.outerHeight();
+            var viewportBottom = $(window).scrollTop() + $(window).height();
+            if (menuBottom > viewportBottom - this.settings['viewportBottomGutter']) {
+                this.$menu.css({
+                    'maxHeight': Math.max(
+                        viewportBottom - this.settings['viewportBottomGutter'] - this.$menu.offset().top,
+                        this.settings['menuMinHeight']
+                    ),
+                    'overflow': 'scroll'
+                });
+            } else {
+                this.$menu.css({
+                    'maxHeight': '',
+                    'overflow': ''
+                });
+            }
+        },
+
+        menuHide: function () {
+            this.$container.removeClass(this.settings['activeClass']);
+            this.$container.removeClass(this.settings['positionedMenuClass']);
+            this.$menu.css('width', 'auto');
+        },
+
+        menuToggle: function () {
+            if (this.$container.hasClass(this.settings['activeClass'])) {
+                this.menuHide();
+            } else {
+                this.menuShow();
+            }
+        }
+
+    });
+
+    $.fn[pluginName] = function (options) {
+        return this.each(function () {
+            if (!$.data(this, "plugin_" + pluginName)) {
+                $.data(this, "plugin_" + pluginName,
+                    new MultiSelect(this, options));
+            }
+        });
+    };
+
+})(jQuery);