Skip to content

6 - Filtering rows

This example shows how to manage a group selection of adjacent seats from a row with filterRows and getNeighbors.

The example includes two html inputs to dynamically change the map based on the user's input.

Map Viewer

Code

// ---- LOADING MODULE ----
DVM.loadModule("map_viewer", {
    container: "viewer-container" // Container where the viewer will be appended
})
    .then(function(viewer) {
        console.log(`Module '${viewer.getModuleName()}' initialized:`, viewer);
        start(viewer);

    })
    .catch(function(err) {
        console.error(err);
    });

function start(viewer) {
    // FLAGS
    var selection = 1; // How many seats are selected at once
    var filter_single_seats = false;

    window.viewer = viewer;
    viewer.subscribe("click", onClick);
    viewer.subscribe("enter", onEnter);
    viewer.subscribe("leave", onLeave);


    // ---- LOADING ----
    viewer.loadMap({
        venue_id: "eu-es-00008-default" // Venue to be loaded. MMC will provide these IDs
    })
        .then(function (obj) {
            // Disable automatic selection and hover
            viewer.flags.automatic_selection = false;
            viewer.flags.automatic_hover = false;

            overrideStyles(viewer);

            // Get sections availability
            var available_sections = getSectionAvailability();
            // Apply sections availabilitfy
            viewer.setAvailability("section", available_sections);

            // Get seats availability
            var available_seats = getSeatAvailability();
            // Apply seats availability
            viewer.setAvailability("seat", available_seats);

            filterRows(selection, filter_single_seats);
            console.log("LOADED!");
        })
        .catch(function (err) {
            // Error while loading
            console.error(err);
        });

    // CALLBACKS
    function onClick(obj) {
        var node = obj.nodes[0];
        if (node && node.type === "seat") {
            if (node.state === "available" || node.state === "selected") {
                var nodes = obj.instance.getNeighbors(node, selection, filter_single_seats);
                if (nodes.length === selection) {
                    obj.instance.unselectAll();
                    obj.instance.select(nodes);
                }
            }
        }
    }

    function onEnter(obj) {
        var node = obj.nodes[0];
        if (node) {
            // If there are neighbors that fulfill the requirements, we hover all the seats, otherwise we hover only the original node.
            if (node.type === "seat" && (node.state === "available" || node.state === "selected")) {
                var nodes = obj.instance.getNeighbors(node, selection, filter_single_seats);
                if (nodes.length === selection) {
                    obj.instance.hover(nodes);
                    return;
                }
            }
            obj.instance.hover(node);
        }

    }

    // called on node mouse leave
    function onLeave(obj) {
        // reset hover nodes
        obj.instance.hover(null);
    }

    // ROWS
    /**
     *
     * This function will filter the nodes that cannot form part of a group of adjacent
     * nodes given a certain 'quantity' and previously applied availability. In addition,
     * we specify with 'filter_single_seats' if selections that leaves single seats
     * are allowed or not.
     *
     * In addition, the function will add the filtered nodes to a group ('filtered')
     * and assign them a tag with the same name to be able to paint them differently on the map.
     *
     * @param {number} quantity - Quantity of adjacent nodes to be selected.
     * @param {boolean} filter_single_seats - if the adjacent nodes may allow single seats (false)
     * or not (true).
     */
    function filterRows(quantity, filter_single_seats) {
        // We make sure that there is no node selected
        viewer.unselectAll();

        // Get all nodes in group 'filtered' from a previous filterRows call
        var old_filtered_nodes = viewer.getNodesByGroups("seat", "filtered", false);
        // Remove nodes from 'filtered' group
        viewer.removeNodesFromGroup(old_filtered_nodes, "filtered");
        // Remove tag 'filtered' from nodes
        viewer.setNodesTag(old_filtered_nodes, null);

        // get the filter nodes
        var filtered_nodes = viewer.filterRows(quantity, filter_single_seats);
        // add nodes to 'filtered' group
        viewer.addNodesToGroup(filtered_nodes, "filtered");
        // Apply 'filtered' tag to nodes
        viewer.setNodesTag(filtered_nodes, "filtered");
    }

    // INPUT ELEMENTS
    var input_selection = document.getElementById("selection_input");
    var input_single_seats = document.getElementById("single_seats_input");


    if (input_selection && input_single_seats) {
        input_single_seats.addEventListener("change", function (e) {
            filter_single_seats = input_single_seats.checked;
            filterRows(selection, filter_single_seats);
        });

        input_selection.addEventListener("input", function (e) {
            var val = parseInt(input_selection.value);
            if (!isNaN(val)) {
                selection = clamp(val, 1, 10);
                input_selection.value = selection;
                viewer.max_selection = selection;
                filterRows(selection, filter_single_seats);
            }
        });
    }

    // ---- AVAILABILITY FUNCTIONS ----
    // Get sections availability. For the purpose, we generate a RANDOM availability.
    function getSectionAvailability() {
        var sections = viewer.getNodesByType("section");
        var available_sections = [];

        for (var i = 0; i < sections.length; ++i) {
            var section = sections[i];
            if (Math.random() < 0.7) {
                available_sections.push(section.id);
            }
        }

        return available_sections;
    }


    // Get seats availability. For the purpose, we generate a RANDOM availability.
    function getSeatAvailability() {
        // Only generate availability for seats with an AVAILABLE parent section
        var available_sections = viewer.getNodesByState("section", "available");
        var available_seats = [];

        for (var i = 0; i < available_sections.length; ++i) {
            var section = available_sections[i];
            var seats = viewer.getNodesByParent(section.id);
            for (var j = 0; j < seats.length; ++j) {
                var seat = seats[j];
                if (Math.random() < 0.7) {
                    available_seats.push(seat.id);
                }
            }
        }

        return available_seats;
    }

    // HELPERS

    // The purpose of this function is just keep the default styles and add to them styles for 'filtered' tag
    function overrideStyles(instance) {
        var styles = instance.getStyles();
        for (var i = 0; i < styles.length; ++i) {
            if (styles[i] && styles[i].seat) {
                if (styles[i].seat.available.normal && styles[i].seat.available.normal.none) {
                    styles[i].seat.available.normal.filtered = JSON.parse(JSON.stringify(styles[i].seat.available.normal.none));
                    styles[i].seat.available.normal.filtered.opacity = 0.4;

                    if (styles[i].seat.available.hover && styles[i].seat.available.hover.none) {
                        styles[i].seat.available.hover.filtered = JSON.parse(JSON.stringify(styles[i].seat.available.normal.none));
                        styles[i].seat.available.hover.filtered.opacity = 0.4;
                    }
                }

            }
        }
        instance.setStyles(styles);
    }
    function clamp(a,b,c){
        return Math.max(b,Math.min(c,a));
    }
}