var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
define(["require", "exports", "jquery", "../domutil", "../key-constants", "bootstrap", "typeahead"], function (require, exports, jquery_1, domutil, keyConstants) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    jquery_1 = __importDefault(jquery_1);
    domutil = __importStar(domutil);
    keyConstants = __importStar(keyConstants);
    /**
     * A typeahead popup GUI element.
     */
    class TypeaheadPopup {
        /**
         * @param doc The DOM document for which to make this popup.
         *
         * @param x Position of popup. The popup may ignore the position if it would
         * overflow off the screen or not have enough space to reasonably show the
         * choices for typing ahead.
         *
         * @param y Position of popup.
         *
         * @param width The desired width of the popup. This value may get overridden.
         *
         * @param  placeholder The placeholder text to use.
         *
         * @param options The options to pass to the underlying Twitter Typeahead
         * menu.
         *
         * @param dismissCallback Function to call when the popup is dismissed.
         */
        // tslint:disable-next-line:max-func-body-length
        constructor(doc, x, y, width, placeholder, options, 
        // tslint:disable-next-line:no-any
        dismissCallback) {
            this.dismissed = false;
            const taWrapper = domutil.htmlToElements("<div class=\"wed-typeahead-popup\">\
<input class=\"typeahead form-control\" type=\"text\">\
<span class=\"spinner\"><i class=\"fa fa-spinner fa-spin\"></i></span></div>", doc)[0];
            const ta = taWrapper.firstElementChild;
            ta.setAttribute("placeholder", placeholder);
            this.taWrapper = taWrapper;
            this.dismissCallback = dismissCallback;
            this.backdrop = document.createElement("div");
            this.backdrop.className = "wed-typeahead-popup-backdrop";
            jquery_1.default(this.backdrop).click(() => {
                this.dismiss();
                return false;
            });
            taWrapper.style.width = `${width}px`;
            taWrapper.style.left = `${x}px`;
            taWrapper.style.top = `${y}px`;
            const $ta = this.$ta = jquery_1.default(ta);
            let args = [options.options];
            if (options.datasets != null && options.datasets.length > 0) {
                args = args.concat(options.datasets);
            }
            // We don't have typings for typeahead
            // tslint:disable-next-line:no-any
            $ta.typeahead.apply($ta, args);
            $ta.on("keydown", this._keydownHandler.bind(this));
            $ta.on("typeahead:selected", this._selectedHandler.bind(this));
            const body = doc.body;
            body.insertBefore(taWrapper, body.firstChild);
            body.insertBefore(this.backdrop, body.firstChild);
            // Verify if we're going to run off screen. If so, then modify our position
            // to be inside the screen.
            const actualWidth = taWrapper.offsetWidth;
            const winWidth = doc.defaultView.innerWidth;
            // The x value that would put the menu just against the side of the window
            // is actualWidth - winWidth. If x is less than it, then x is the value we
            // want, but we don't want less than 0.
            taWrapper.style.left = `${Math.max(0, Math.min(x, winWidth -
                actualWidth))}px`;
            taWrapper.style.maxWidth = `${winWidth}px`;
            const winHeight = doc.defaultView.innerHeight;
            const maxHeight = winHeight - y;
            taWrapper.style.maxHeight = `${maxHeight}px`;
            const dropdown = taWrapper.getElementsByClassName("tt-menu")[0];
            const $dropdown = jquery_1.default(dropdown);
            // Yep, we forcibly display it here because the next computations depend on
            // the dropdown being visible.
            const oldDisplay = dropdown.style.display;
            dropdown.style.display = "block";
            // We arbitrarily want to show at least five lines of information. (Which
            // may or may not translate to 4 choices. This is not the goal. The goal is
            // just to show a reasonable amount of information.)
            const fiveLines = Number($dropdown.css("line-height").replace("px", "")) * 5;
            const dropdownPos = dropdown.getBoundingClientRect();
            let dropdownMaxHeight = winHeight - dropdownPos.top;
            if (dropdownMaxHeight < fiveLines) {
                // Less than 5 lines: we need to move up.
                y -= fiveLines - dropdownMaxHeight;
                dropdownMaxHeight = fiveLines;
                taWrapper.style.top = `${y}px`;
            }
            dropdown.style.maxHeight = `${dropdownMaxHeight}px`;
            // Restore it. It was probably hidden.
            dropdown.style.display = oldDisplay;
            // Work around a stupid issue with typeahead. The problem is that
            // **hovering** over a choice makes it so that the choice is considered to
            // be the one to be selected when ENTER is pressed. This can lead to
            // inconsistent behavior from browser to browser. (It certainly messed up
            // testing.)
            $dropdown.off("mouseenter.tt", ".tt-suggestion");
            $dropdown.off("mouseleave.tt", ".tt-suggestion");
            // Prevent clicks from propagating up.
            $dropdown.on("click", false);
            ta.focus();
            // Typeahead will consider itself "activated" once it is focused. On most
            // platforms the focus above is delivered right away. However, on IE the
            // focus event is sent to elements asynchronously. Which means that the
            // typeahead could become "activated" much later than the end of this
            // constructor. For our purposes we want the typeahead to be activated right
            // away. So we unfortunately break through into private bits of the
            // typeahead code.
            const tt = jquery_1.default.data(ta, "ttTypeahead");
            tt.isActivated = true;
            // The default implementation closes the dropdown when the input is
            // unfocused. This is not a particularly good behavior for
            // wed. Unfortunately, the only way to rectify it is to break into the
            // private parts of typeahead.
            tt.input.off("blurred");
            tt._onBlurred = function _onBlurred() {
                this.isActivated = false;
            };
            tt.input.onSync("blurred", tt._onBlurred, tt);
        }
        /**
         * Dismisses the popup. Calls the callback that was passed when the popup was
         * created, if any.
         *
         * @param obj This should be the object selected by the user, if any. This
         * will be passed to the ``dismissCallback`` that was passed when the popup
         * was created, if any. If you call this method directly and want a selection
         * to occur, take care to use an object which is from the data set passed in
         * the ``options`` parameter that was used when the popup was created. The
         * value ``undefined`` means no object was selected.
         */
        // tslint:disable-next-line:no-any
        dismiss(obj) {
            if (this.dismissed) {
                return;
            }
            const taWrapper = this.taWrapper;
            if (taWrapper !== undefined && taWrapper.parentNode !== null) {
                taWrapper.parentNode.removeChild(taWrapper);
            }
            const backdrop = this.backdrop;
            if (backdrop !== undefined && backdrop.parentNode !== null) {
                backdrop.parentNode.removeChild(backdrop);
            }
            if (this.dismissCallback !== undefined) {
                this.dismissCallback(obj);
            }
            this.dismissed = true;
        }
        /**
         * Event handler for keydown events on the popup. The default implementation
         * is to dismiss the popup if escape is pressed.
         */
        _keydownHandler(ev) {
            if (keyConstants.ESCAPE.matchesEvent(ev)) {
                this.dismiss();
                return false;
            }
            return undefined;
        }
        /**
         * Event handler for typeahead:selected events. The default implementation is
         * to dismiss the popup.
         */
        // tslint:disable-next-line:no-any
        _selectedHandler(_ev, obj) {
            this.dismiss(obj);
        }
        /**
         * Hide the spinner that was created to indicate that the data is being
         * loaded.
         */
        hideSpinner() {
            this.taWrapper.getElementsByClassName("spinner")[0]
                .style.display = "none";
        }
        /**
         * Set the value in the input field of the typeahead. This also updates the
         * suggestions.
         *
         * @param value The new value.
         */
        setValue(value) {
            // tslint:disable-next-line:no-any
            this.$ta.typeahead("val", value);
        }
    }
    exports.TypeaheadPopup = TypeaheadPopup;
});
//  LocalWords:  typeahead MPL px keydown actualWidth winWidth tt dropdown
//  LocalWords:  dropdownMaxHeight mouseenter mouseleave ttTypeahead
//# sourceMappingURL=typeahead-popup.js.map