curtain.reveal = function()

in modules/ui/curtain.js [69:235]


    curtain.reveal = function(box, html, options) {
        options = options || {};

        if (typeof box === 'string') {
            box = d3_select(box).node();
        }
        if (box && box.getBoundingClientRect) {
            box = copyBox(box.getBoundingClientRect());
            var containerRect = containerNode.getBoundingClientRect();
            box.top -= containerRect.top;
            box.left -= containerRect.left;
        }
        if (box && options.padding) {
            box.top -= options.padding;
            box.left -= options.padding;
            box.bottom += options.padding;
            box.right += options.padding;
            box.height += options.padding * 2;
            box.width += options.padding * 2;
        }

        var tooltipBox;
        if (options.tooltipBox) {
            tooltipBox = options.tooltipBox;
            if (typeof tooltipBox === 'string') {
                tooltipBox = d3_select(tooltipBox).node();
            }
            if (tooltipBox && tooltipBox.getBoundingClientRect) {
                tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
            }
        } else {
            tooltipBox = box;
        }

        if (tooltipBox && html) {

            if (html.indexOf('**') !== -1) {
                if (html.indexOf('<span') === 0) {
                    html = html.replace(/^(<span.*?>)(.+?)(\*\*)/, '$1<span>$2</span>$3');
                } else {
                    html = html.replace(/^(.+?)(\*\*)/, '<span>$1</span>$2');
                }
                // pseudo markdown bold text for the instruction section..
                html = html.replace(/\*\*(.*?)\*\*/g, '<span class="instruction">$1</span>');
            }

            html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');   // emphasis
            html = html.replace(/\{br\}/g, '<br/><br/>');       // linebreak

            if (options.buttonText && options.buttonCallback) {
                html += '<div class="button-section">' +
                    '<button href="#" class="button action">' + options.buttonText + '</button></div>';
            }

            var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
            tooltip
                .classed(classes, true)
                .selectAll('.popover-inner')
                .html(html);

            if (options.buttonText && options.buttonCallback) {
                var button = tooltip.selectAll('.button-section .button.action');
                button
                    .on('click', function(d3_event) {
                        d3_event.preventDefault();
                        options.buttonCallback();
                    });
            }

            var tip = copyBox(tooltip.node().getBoundingClientRect()),
                w = containerNode.clientWidth,
                h = containerNode.clientHeight,
                tooltipWidth = 200,
                tooltipArrow = 5,
                side, pos;


            // hack: this will have bottom placement,
            // so need to reserve extra space for the tooltip illustration.
            if (options.tooltipClass === 'intro-mouse') {
                tip.height += 80;
            }

            // trim box dimensions to just the portion that fits in the container..
            if (tooltipBox.top + tooltipBox.height > h) {
                tooltipBox.height -= (tooltipBox.top + tooltipBox.height - h);
            }
            if (tooltipBox.left + tooltipBox.width > w) {
                tooltipBox.width -= (tooltipBox.left + tooltipBox.width - w);
            }

            // determine tooltip placement..

            if (tooltipBox.top + tooltipBox.height < 100) {
                // tooltip below box..
                side = 'bottom';
                pos = [
                    tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
                    tooltipBox.top + tooltipBox.height
                ];

            } else if (tooltipBox.top > h - 140) {
                // tooltip above box..
                side = 'top';
                pos = [
                    tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
                    tooltipBox.top - tip.height
                ];

            } else {
                // tooltip to the side of the tooltipBox..
                var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;

                if (localizer.textDirection() === 'rtl') {
                    if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
                        side = 'right';
                        pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];

                    } else {
                        side = 'left';
                        pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
                    }

                } else {
                    if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
                        side = 'left';
                        pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
                    } else {
                        side = 'right';
                        pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
                    }
                }
            }

            if (options.duration !== 0 || !tooltip.classed(side)) {
                tooltip.call(uiToggle(true));
            }

            tooltip
                .style('top', pos[1] + 'px')
                .style('left', pos[0] + 'px')
                .attr('class', classes + ' ' + side);


            // shift popover-inner if it is very close to the top or bottom edge
            // (doesn't affect the placement of the popover-arrow)
            var shiftY = 0;
            if (side === 'left' || side === 'right') {
                if (pos[1] < 60) {
                    shiftY = 60 - pos[1];
                } else if (pos[1] + tip.height > h - 100) {
                    shiftY = h - pos[1] - tip.height - 100;
                }
            }
            tooltip.selectAll('.popover-inner')
                .style('top', shiftY + 'px');

        } else {
            tooltip
                .classed('in', false)
                .call(uiToggle(false));
        }

        curtain.cut(box, options.duration);

        return tooltip;
    };